Проектирование и разработка подсистемы управления транзакциями для АСУД 'ЕВФРАТ'

  • Вид работы:
    Дипломная (ВКР)
  • Предмет:
    Информационное обеспечение, программирование
  • Язык:
    Русский
    ,
    Формат файла:
    MS Word
    146,17 Кб
  • Опубликовано:
    2013-03-14
Вы можете узнать стоимость помощи в написании студенческой работы.
Помощь в написании работы, которую точно примут!

Проектирование и разработка подсистемы управления транзакциями для АСУД 'ЕВФРАТ'

Оглавление

1. Введение

. Специальная часть

.1 Анализ и обзор существующих решений

.1.1 Механизмы управления транзакциями в СУБД

.1.2 Системы управления распределенными транзакциями

.1.3 Управление транзакциями в среде .NET

.1.4 Средства удаленного взаимодействия с объектами

.1.5 Выводы

.2 Постановка задач

.3 Разработка подсистемы управления транзакциями

.3.1 Предлагаемая модель подсистемы управления транзакциями

.3.2 Модель производительности подсистемы

.3.3 Практическая реализация подсистемы

.3.4 Практический анализ производительности подсистемы

.3.5 Рекомендации по использованию

.4 Выводы

. Экологическая часть и безопасность жизнедеятельности

.1 Исследование воздействия опасных и вредных факторов при эксплуатации ЭВМ и их воздействие на организм человека

.2 Способы защиты пользователей от опасных и вредных факторов

.3 Выводы

Заключение

Список литературы

Приложения

1. Введение

Один из способов увеличения производительности вычислительных систем - это переход от одноядерной процессорной архитектуры к многоядерной. Многоядерные процессоры позволяют увеличить количество инструкций, выполняемое за единицу времени. Однако производительность так же будет зависеть от используемого приложения и его оптимизации для выполнения на многоядерном процессоре. Чтобы приложение могло одновременно задействовать несколько процессорных ядер, оно должно хорошо распараллеливаться, иначе, если программный код подразумевает только последовательное выполнение, пользы от многоядерности не будет.

С каждым годом в мире компьютерной индустрии возрастает значимость так называемого «промежуточного программного обеспечения». Существует множество продуктов этой категории, производимых крупными, средними и даже мелкими компаниями, специализирующимися на программном обеспечении. В настоящее время термин «промежуточное программное обеспечение» («middleware») относится к любому программному компоненту, который располагается между пользовательскими приложениями на персональных компьютерах и РСУБД или унаследованной системой, непосредственно управляющими необходимыми данными.

Управление транзакциями - один из важнейших аспектов при создании надежных распределенных систем, хотя в различных технологиях создания таких систем степень универсальности управления транзакциями может существенно варьироваться.

Распределенная система является совокупностью взаимодействующих объектов, в общем случае имеющих состояния. Важнейшим отличием распределенных (многозвенных) систем от классических клиент-серверных приложений является то, что при начале транзакции в принципе неизвестно, сколько объектов, состояние которых нужно сохранять или восстанавливать по обычным правилам транзакций, принимают в этой транзакции участие. Клиент, посылающий запрос серверному объекту, в общем случае не знает, какую цепочку вызовов (и к каким объектам) породит этот единственный запрос. О цепочке вызовов в целом не имеет представление ни один из участвующих в ней объектов.

2. Специальная часть


.1 Анализ и обзор существующих решений

На сегодняшний день к информационным системам предъявляются жесткие требования, как производительности, так и надежности. В связи с этим существует необходимость предотвращения рассогласования данных, порождаемого параллельной работой нескольких пользователей с одними и теми же данными. Кроме того, не менее важно, чтобы никакие отказы и сбои не порождали рассогласование данных информационной системы. На данный момент наиболее распространены механизмы контроля операций с данными информационной системы посредством транзакций, как операций записи данных, так и чтения.

.1.1 Механизмы управления транзакциями в СУБД

Для обеспечения целостности базы данных в классических СУБД используется механизм транзакций. Все операции с базой данных осуществляются только от лица транзакций, чье выполнение контролируется специальным алгоритмом, гарантирующим сохранение целостности базы. Алгоритмы управления транзакциями регулируют порядок совместной работы нескольких транзакций и поэтому обычно называются протоколами управления транзакциями. Такие протоколы основываются на предположении, что каждая из управляемых ими транзакций обладает некоторым заданным набором свойств. Разные протоколы могут опираться на разные наборы свойств транзакций, но наиболее стандартным является так называемый набор свойств ACID:

·        Atomicity (атомарность): определяет, что транзакция является наименьшим, неделимым блоком алгоритма изменения данных. Другими словами, любые части (подоперации) транзакции либо выполняются все, либо не выполняется ни одной такой части. Поскольку на самом деле невозможно одновременно и атомарно выполнить последовательность команд внутри транзакции, вводится понятие «отката» (rollback): если транзакцию не удаётся полностью завершить, результаты всех до сих пор произведённых действий должны быть отменены и система возвращается в исходное состояние;

·        Consistency (непротиворечивость): по окончанию транзакция оставляет данные в непротиворечивом состоянии;

·        Isolation (изоляция): во время выполнения транзакции другие процессы не должны видеть данные в промежуточном состоянии;

·        Durability (постоянство): независимо от проблем на нижних уровнях (к примеру, обесточивание системы или сбои в оборудовании) изменения, сделанные успешно завершённой транзакцией, останутся сохранёнными после возвращения системы в работу.

Обязательным требованием, предъявляемым к любому протоколу управления транзакциями, является условие сохранения целостного состояния базы данных при совместном выполнении транзакций. Основным критерием определения сохранения целостности базы и, следовательно, корректности работы протокола является критерий сериализуемости получающихся расписаний[1].

Все протоколы управления транзакциями подразделяются на два класса: пессимистические и оптимистические.

Отличительной особенностью протоколов пессимистического класса является предотвращение возможности возникновения конфликтов. Основным методом для достижения этого является механизм блокировок. Этот метод предполагает, что для использования какого-либо ресурса транзакции необходимо сначала получить блокировку на этот ресурс. Обладание блокировкой на данный ресурс гарантирует невозможность конфликта с другими транзакциями, которые хотят использовать тот же ресурс. Транзакциям, которые потенциально могут вызвать появление конфликта с держателем блокировки на ресурс, блокировка просто не выдается. В таком случае эта транзакция встает в очередь на получение блокировки на этот ресурс и продолжает работу после получения этой блокировки.

Одним из наиболее широко распространенных пессимистических протоколов является «двухфазный протокол» (2PL)[2], согласно которому все операции блокирования ресурсов должны предшествовать всем операциям разблокирования. В классическом варианте этого протокола рассматриваются блокировки двух видов - на чтение и на изменение.

Серьезная проблема, которая может возникнуть при применении пессимистического протокола, - это так называемая проблема возникновения «тупиков» (deadlock). Тупик - это такая ситуация, при которой каждая транзакция из некоторого множества ожидает получения блокировки на элемент, который в данный момент времени заблокирован другой транзакцией из этого множества. Попавшие в ситуацию тупика транзакции самостоятельно покинуть его не в силах и могут ждать вечно. Для борьбы с тупиками используются специальные методы их обнаружения и разрешения.

Основная идея оптимистических протоколов - максимизировать объем выполняемой работы. Эти протоколы дают шанс работать всем транзакциям, но разрешают благополучно завершиться только тем из них, чье завершение не влечет нарушения целостности базы.

С точки зрения менеджера управления транзакций при использовании оптимистического протокола выполнение транзакции делится на три концептуальные фазы: чтение, проверка и запись. В течение фазы чтения транзакция работает параллельно с другими транзакциями без каких-либо ограничений, но все измененные данные записываются в личную рабочую память транзакции, а не в базу. Когда транзакция завершает свою работу менеджер инициирует фазу проверки транзакции на наличие конфликтов с другими транзакциями. Если проверка окончилась удачно (т. е. конфликтов не обнаружено), то сделанные транзакцией изменения переносятся в базу и становятся видимыми другим транзакциям. В противном случае, сама транзакция или конфликтующие с ней транзакции завершаются анормально, т.е. сделанные изменения забываются (поведение в этом случае может быть специфично для конкретного протокола).

По типу проверки оптимистические протоколы делятся на «вперед смотрящие»[3] (forward validation) и «назад смотрящие»[4] (backward validation). Различие состоит в выборе множества транзакций для проверки на наличие конфликтов при завершении некоторой транзакции. Протоколы из первой группы используют в качестве такого множества все еще не завершенные транзакции (и анормально завершают все те из них, которые конфликтуют с целевой транзакцией, либо саму транзакцию). А «назад смотрящие» проводят проверку по отношению ко всем нормально завершившимся транзакциям и в случае конфликта анормально завершают только целевую транзакцию.

Основным недостатком пессимистического подхода являются простои во время тупиков и растрата машинных ресурсов на их обнаружение и разрешение. Оптимистический подход бесполезно тратит машинные ресурсы на работу транзакций, которые позже будут анормально завершены из-за возникновения конфликтов, а также на работу на стадии проверки при завершении каждой транзакции. Экспериментальные результаты показывают, что в случае классических СУБД пессимистический подход обычно показывает лучшие результаты[5,6]. Поэтому в большинстве современных промышленных СУБД используются разновидности пессимистических протоколов.

В отличие от классических СУБД в системе реального времени с каждой транзакцией ассоциируется директивный срок, т.е. момент времени, до которого транзакция должна быть завершена. По типу директивных сроков СУБД реального времени можно разделить на три основные группы: с жесткими, крепкими и мягкими директивными сроками[7]. В системе с жесткими директивными сроками любая задержка эквивалентна катастрофе, а с крепкими и мягкими директивными сроками только понижает производительность системы. Различие последних состоит в том, что в системе с крепкими директивными сроками транзакция, пропустившая свой директивный срок, выкидывается из системы, а в системе с мягкими директивными сроками такая транзакция просто становится менее значимой, но все еще может быть с пользой завершена.

По достижении директивного срока в системах первого типа транзакция становится «вредной», т.е. имеет отрицательную полезность. Если же директивные сроки крепкие, то транзакция просто становится бесполезной, но не «вредной». А в системах с мягкими директивными сроками полезность транзакции начинает постепенно уменьшаться с увеличением времени опоздания.

Еще одним важным отличием от случая классических СУБД является использование других критериев производительности. В классических СУБД, производительность обычно измеряется как среднее число завершившихся транзакций в единицу времени, а в системе, работающей в реальном времени, на первый план выходят другие параметры - количество транзакций, пропустивших свои диpективные сроки, среднее опоздание транзакций и т. п. Производительность же обычно меряют при помощи величины, которая зависит от нескольких таких параметров.

Способы вычисления этих величин сильно отличаются для разных типов СУБД реального времени. Так, например, в системах с крепкими директивными сроками среднее время опоздания транзакций не играет никакой роли, в то время как в системах с мягкими директивными сроками этот параметр довольно полезен.

Отличие критериев производительности, используемых в базах данных реального времени, от тех, что используются в классических СУБД, является основной причиной далеко не лучшей производительности при применении в системах реального времени классических протоколов управления транзакциями. Это, очевидным образом, влечет необходимость использования в таких системах других протоколов, которые лучше справляются со специфическими требованиями систем реального времени.

Используемые в СУБД реального времени протоколы должны использовать приоритеты транзакций, основанные на их директивных сроках, при разрешении конфликтов, для того, чтобы гарантировать, что транзакция с большим приоритетом не будет задержана транзакцией с меньшим приоритетом. Это требование совершенно не учитывается в классических протоколах и это является одной из основных причин их неудовлетворительной производительности.

Наиболее известными пессимистическими протоколами для баз данных в системах реального времени являются модификации 2PL. Рассмотрим вкратце некоторые из них.

Основная идея первой модификации, называемой 2PL-HP (High Priority)[8] - разрешать все конфликты в пользу транзакций с большим приоритетом. Если ресурс, на который транзакция запрашивает блокировку, свободен, транзакция получает блокировку. Если он заблокирован, то возможны варианты:

·        если запрашиваемая блокировка не конфликтует с уже существующими блокировками других транзакций на означенный ресурс, то она выдается, если приоритет транзакции выше, чем у всех транзакций, стоящих в очереди на блокировки этого ресурса;

·        если транзакция имеет приоритет выше, чем у всех держателей блокировок на ресурс, то все они должны быть оборваны и транзакция получает блокировку;

·        в остальных случаях транзакция становится в очередь на эту блокировку.

Протокол 2PL-HP гарантирует отсутствие тупиков.

Другая модификация - 2PL-WP (Wait Promote)[9] - основана на идее наследования приоритета. Когда первая транзакция с большим приоритетом запрашивает блокировку на какой-то ресурс, который в данный момент заблокирован второй транзакцией с меньшим приоритетом, то она встает в очередь (как в 2PL), при этом второй транзакции повышают приоритет до уровня приоритета первой. В результате вторая транзакция может быть выполнена быстрее, поскольку ей меньше придется простаивать дожидаясь других ресурсов, и, следовательно, приближается момент получения блокировки на означенный ресурс первой транзакцией. Однако, при применении этого метода (из-за рекурсивного повышения приоритетов) легко может возникнуть ситуация, когда большинство или даже все транзакции имеют одинаковый приоритет. В таком случае поведение этого протокола будет мало отличаться от поведения классического 2PL.

Большинство оптимистических протоколов, применяемых в СУБД реального времени, относятся к классу «вперед смотрящих». Для повышения производительности в СУБД реального времени возникающие между транзакциями конфликты должны разрешаться в пользу транзакций с большим приоритетом, а это невозможно при применении «назад смотрящих» оптимистических протоколов, поскольку в этом случае конфликты возникают с уже завершившимися транзакциями. Рассмотрим примеры оптимистических протоколов для систем реального времени.

Протокол OPT-SACRIFICE[10] является адаптированным к СУБД реального времени вариантом протокола OCC-FV (Optimistic Concurrency Control with Forward Validation). Согласно OPT-SACRIFICE транзакция, достигшая фазы проверки, обрывается, если хотя бы одна из конфликтующих с ней транзакций имеет больший приоритет. Иначе транзакция благополучно завершается, а все конфликтующие с ней транзакции завершаются анормально. Таким образом, проверяемая транзакция, которая уже почти завершилась, приносит себя в жертву ради еще работающей транзакции с большим приоритетом. Этот протокол имеет ряд слабых мест. Обрыв транзакции в пользу более высокоприоритетной означает, что работа по ее выполнению была выполнена напрасно и ресурсы, потраченные на это, были потрачены зря. Кроме того, не гарантирован факт нормального завершения более высокоприоритетной транзакции, таким образом жертва может оказаться напрасной.

Согласно протоколу OPT-WAIT[10], также принадлежащему к классу «вперед смотрящих» протоколов, транзакция, достигнув фазы проверки и обнаружив множество конфликтующих с ней транзакций, ведет себя следующим образом:

·              если ее приоритет выше, чем у всех конфликтующих с ней транзакций, то она завершается нормально, а все конфликтующие - анормально;

·              иначе транзакция не обрывается немедленно, а ждет завершения тех из конфликтующих, чей приоритет выше ее собственного, и затем завершается нормально только в том случае, если все эти конфликтующие завершились анормально.

Этот протокол, в отличие от OPT-SACRIFICE, не отличается обилием бесполезных рестартов, так как все они совершаются по необходимости. Однако он также имеет ряд слабых мест. Эффект блокирования, возникающий в результате задержки транзакции на стадии проверки (вместо ее непосредственного завершения или обрыва), приводит к консервированию ресурсов, тем самым приводя к тем проблемам, с которыми сталкиваются пессимистические протоколы в системах реального времени. Кроме этого, даже в случае успешного завершения задержанной транзакции, это может вызвать анормальное завершение множества более низкоприоритетных транзакций (как работающих, так и задержанных). Более того, число анормальных завершений может возрасти за счет тех низкоприоритетных транзакций, которые конфликтовали с задержанной транзакцией, но не конфликтовали с более высокоприоритетными. Это число может сильно расти с ростом времени задержки нашей транзакции.

Традиционно протоколы управления транзакциями различаются по двум критериям: время обнаружения конфликтов и способ их обнаружения. Согласно этим критериям пессимистические и оптимистические протоколы представляют собой две крайности. Пессимистические протоколы пытаются обнаружить конфликты сразу, еще до их возникновения, и разрешают их, используя механизм блокирования. Оптимистические протоколы занимаются обнаружением конфликтов при завершении транзакции и разрешают их, перезапуская часть конфликтующих транзакций. Оба способа разрешения конфликтов имеют свои недостатки. Блокирование приводит к консервированию ресурсов, а рестарты вызывают их растрату. В системах реального времени протокол управления транзакциями должен работать с большим количеством анормально завершающихся транзакций. И, если для оптимистических протоколов это знакомая ситуация, то для пессимистических протоколов рестарты являются новым ограничением.

Слабым местом пессимистического протокола являются также простои транзакций, в ожидании получения блокировки на какой-либо ресурс. Такие простои повышают вероятность пропуска директивного срока. При этом транзакция, которая держала блокировку на нужный ресурс, также может пропустить свой директивный срок - из-за того, что он слишком близок или из-за необходимости ждать получения блокировок на другие ресурсы. Тем самым возможно, что все транзакции не успеют завершиться к своим директивным срокам.

Оптимистические протоколы не блокируют ресурсы и не вызывают простоев транзакций, что несомненно является плюсом для систем реального времени. Обратная сторона медали - растрата ресурсов на параллельную работу конфликтующих транзакций, из которых только часть в принципе может завершиться. Кроме того необходимо отметить, что фаза проверки может быть довольно трудоемкой и это особенно заметно, если сами транзакции очень коротки.

Сравнительный анализ этих двух подходов для модели простых транзакций в системах управления базами данных реального времени с мягкими и крепкими директивными сроками, с ограниченным числом ресурсов и неограниченным, показывает, что в большинстве ситуаций оптимистический подход превосходит пессимистический. Однако, в связи с огромным количеством систем реального времени со специфическими требованиями не существует лучшего протокола для всех случаев, и бывают ситуации, когда пессимистический протокол показывает лучшие результаты.

В идеале транзакции разных пользователей должны выполняться так, чтобы создавалась иллюзия, что пользователь текущей транзакции - единственный. В этом заключается реализация свойства Isolation из набора ACID, которая не обязательна, но в случае пренебрежения ею может повлечь появление проблем. В реальности, по соображениям производительности и для выполнения некоторых специальных задач, СУБД предоставляют различные уровни изоляции транзакций: неподтвержденное или грязное чтение (Read Uncommited), подтвержденное чтение (Read Commited), повторяемое чтение (Repeatable Read, Snapshot) и самый высокий - упорядоченный уровень (Serializable). Чем выше уровень изоляции, тем больше требуется ресурсов, чтобы их поддерживать. В СУБД уровень изоляции транзакций можно выбрать как для всех транзакций сразу, так и для некоторой конкретной транзакции. Подробнее об уровнях изоляции речь пойдет позже.

Полноценная реализация уровней изоляции и свойств ACID представляет из себя нетривиальную задачу. Обработка поступающих данных приводит к большому количеству маленьких изменений, включая обновление как самих таблиц, так и индексов. Эти изменения потенциально могут потерпеть неудачу, например если закончилось место на диске, операция занимает слишком много времени (timeout) и т. д. Система должна в случае неудачи корректно вернуть базу данных в состояние до транзакции.

Первые коммерческие СУБД (к примеру, IBM DB2), пользовались исключительно механизмом блокировок доступа к данным для реализации свойств ACID. Но большое количество блокировок приводит к существенному уменьшению производительности. Есть два популярных семейства решений этой проблемы, которые снижают количество блокировок: журнализация изменений (Write Ahead Logging, WAL) и механизм теневых страниц (Shadow Paging). В обоих случаях, блокировки должны быть расставлены на всю информацию, которая обновляется. В зависимости от уровня изоляции и имплементации, блокировки записи также расставляются на информацию, которая была прочитана транзакцией.

При «упреждающей журнализации», используемой в Sybase и MS SQL Server до версии 2005, записи о некоторой операции над базой данных попадают на энергонезависимый носитель (обычно в этой роли выступает жесткий диск) раньше, чем в базу вносятся изменения, произведенные этой операцией. Это позволяет СУБД вернуться в рабочее состояние после неожиданного отказа системы.

Альтернативной техникой, используемой для восстановления в случае сбоев, является механизм «теневых страниц». Основное отличие систем, использующих теневые страницы, от систем, где для восстановления используется только журнал, заключается в том, что последние записывают изменения, внесенные в страницу базы данных в то же место, где располагалась предыдущая версия данных. При использовании же теневых страниц, измененная версия страницы данных записывается на другое место диска. В случае сбоя можно восстановить базу данных на основе старой (теневой) версии страницы и журнала. Как видно, идея, стоящая за теневым механизмом, проста и легко поддается реализации. Однако по ряду причин техника восстановления на основе теневых страниц, несмотря на всю свою простоту, считается хуже, чем восстановление с использованием упреждающей журнализации.

Дальнейшее развитие СУБД привело к появлению так называемых «безблокировочных» технологий. Идея контроля за параллельным доступом с помощью временных меток (Timestamp-Based Concurrency Control) была развита и привела к появлению многоверсионной архитектуры MVCC. Управление конкурентным доступом с помощью многоверсионности (MVCC - MultiVersion Concurrency Control) заключается в предоставлении каждому пользователю так называемого «снимка» БД, обладающего тем свойством, что вносимые данным пользователем изменения в БД невидимы другим пользователям до момента фиксации транзакции. Этот способ управления позволяет добиться того, что пишущие транзакции не блокируют читающих, а читающие транзакции не блокируют пишущих. Эти технологии не нуждаются ни в журнализации изменений, ни в теневых страницах. Архитектура, реализованная в Oracle версии 7.х и выше, записывает старые версии страниц в специальный «сегмент отката», но они все ещё доступны для чтения. Если транзакция при чтении попадает на страницу, временная метка которой новее начала чтения, данные берутся из сегмента отката (то есть используется «старая» версия). Для поддержки такой работы ведётся журнал транзакций, но в отличие от «упреждающей журнализации», он не содержит данных. Работа с ним состоит из трёх логических шагов:

.        Записать намерение произвести некоторые операции;

.        Выполнить задание, копируя оригиналы изменяемых страниц в сегмент отката;

.        Записать, что всё сделано без ошибок.

Журнал транзакций в сочетании с сегментом отката (область, в которой хранится копия всех изменяемых в ходе транзакции данных) гарантирует целостность данных. В случае сбоя запускается процедура восстановления, которая просматривает отдельные его записи следующим образом:

·              Если повреждена запись, то сбой произошёл во время проставления отметки в журнале. Значит, ничего важного не потерялось, игнорируем эту ошибку;

·              Если все записи помечены как успешно выполненные, то сбой произошёл между транзакциями, здесь также нет потерь;

·              Если в журнале есть незавершённая транзакция, то сбой произошёл во время записи на диск. В этом случае мы восстанавливаем старую версию данных из сегмента отката.вообще не имеет ни журнала изменений, ни сегмента отката, а реализует MVCC, записывая новые версии строк прямо в активное пространство данных. Также поступает MS SQL 2005. Теоретически это даёт максимальную эффективность при параллельной работе с данными, но ценой является необходимость «сборки мусора», то есть удаления старых и уже не нужных версий строк.

2.1.2 Системы управления распределенными транзакциями

Существует два главных аспекта управления транзакциями, а именно: управление восстановлением и управление параллельностью обработки. Оба этих аспекта имеют расширенную трактовку в среде распределенных систем. Чтобы разъяснить особенности этой расширенной трактовки, сначала необходимо ввести новое понятие «агент». В распределенной системе отдельная транзакция может включать в себя выполнение кода на многих узлах[11]. Поэтому говорят, что каждая транзакция содержит несколько агентов, где под агентом подразумевается процесс, который выполняется для данной транзакции на отдельном узле. Система должна знать, что два агента являются элементами одной и той же транзакции, например два агента, которые являются частями одной и той же транзакции, очевидно, не должны оказаться в состоянии взаимной блокировки.

Теперь обратимся непосредственно к управлению восстановлением. Чтобы обеспечить атомарность транзакции (принцип «все или ничего») в распределенной среде, система должна гарантировать, что все множество относящихся к данной транзакции агентов или зафиксировало свои результаты, или выполнило откат. Такого результата можно достичь с помощью «двухфазного протокола фиксации» транзакции.

Что касается управления параллельностью, то оно в большинстве распределенных систем базируется на механизме блокирования, точно так же, как и в не распределенных системах. В нескольких более новых коммерческих продуктах была реализована многовариантная блокировка данных, однако на практике обычное блокирование еще, кажется, по-прежнему остается тем методом, который выбирается в большинстве систем.

Распределенные объектные системы обеспечивают средства размещения и взаимодействия объектов в распределенной среде. Объекты идентифицируются именами или сервисами, а также поддерживаемыми ими интерфейсами. Реализация объекта и платформа, на которой он выполняется, прозрачны для клиента.

Во многих организациях используется более чем одна система баз данных, и требуется возможность выполнять транзакции, пересекающие границы этих систем. Многие системы баз данных требуют наличия отдельного процесса операционной системы для каждого подключенного пользователя. Для приложений с сотнями пользователей не хватает мощности даже самых крупных компьютеров. Кроме всего прочего, установление соединения с базой данных часто происходит медленно. Если много пользователей часто подключается и отключается, производительность системы серьезно деградирует. Эти проблемы решаются в большинстве систем, управляющих распределенными транзакциями, следующим образом:

·              Обеспечивается одновременная связь с набором различных систем баз данных;

·              Поддерживается двухфазный протокол фиксации, гарантирующий завершение транзакций над несколькими базами данных;

·              Пользовательские запросы обрабатываются с использованием легковесных нитей - потоков (threads) операционной системы, а не полновесных процессов. Это позволяет использовать возможности SMP-систем (Symmetric MultiProcessor), таких как Sun Enterprise, Digital Alpha и Compaq Proliant;

·              Поддерживается постоянный пул подключений к базам данных, и эти подключения разделяются между пользователями. В большинстве приложений каждый пользователь реально обращается к базе данных только часть общего времени. Часто сотни «одновременно работающих» пользователей могут быть эффективно обслужены за счет наличия одной трети или даже одной десятой от числа подключений к базе данных, требуемых для прямого доступа;

·              Долговременное сохранение разделяемых подключений к базе данных существенно уменьшает трафик подключений;

·              Обеспечивается балансировка нагрузки путем планирования использования разделяемых ресурсов и направления запросов к наименее загруженным серверам. Возможно, также обнаруживать и обрабатывать ситуации, когда сервер или другой ресурс выходит из строя и нуждается в перезапуске;

·              Запросы обрабатываются асинхронно с распределением нескольких запросов к одному и тому же серверу между разными подключениями к базе данных (так называемый «конвейерный» параллелизм);

·              Запросы распределяются между несколькими серверами баз данных (так называемый «развернутый» параллелизм).

Для координации операций разделенных и распределенных приложений поддерживается одноранговая связь с другими мониторами обработки транзакций.

.1.3 Управление транзакциями в среде .NET

Все или ничего - в этом главный смысл транзакции. При сохранении нескольких записей либо все они должны быть записаны, либо вся операция должна быть отменена. Если происходит один единственный сбой при внесении одной записи, то все, что было выполнено к этому моменту в пределах данной транзакции, откатывается. Транзакции широко используются при работе с базами данных, но классы из пространства имен System.Transaction библиотеки классов .NET Framework позволяют выполнять транзакции с изменчивыми или находящимися в памяти объектами, такими как списки объектов[12]. Если список поддерживает транзакции, объект добавляется или удаляется и транзакция завершается неудачей, то все операции со списком автоматически отменяются. Запись в списки, находящиеся в памяти может выполняться в той же транзакции, что и запись в базу данных.

Наиболее распространено применение транзакций при внесении и обновлении информации в базе данных. Транзакции также могут выполняться при записи сообщения в очередь сообщений или при записи данных в файл или системный реестр. Несколько действий могут быть частями одной транзакции. Транзакция характеризуется набором свойств ACID, подробно описанным ранее. В типовом случае транзакции управляются и координируются диспетчером транзакций. Любой ресурс, влияющий на исход транзакции, управляется диспетчером ресурсов. Диспетчер транзакций взаимодействует с диспетчерами ресурсов, определяя исход транзакции.

Выделяют следующие фазы времени выполнения транзакции: активная, подготовительная и фаза фиксации. Во время активной фазы транзакция создается, диспетчеры ресурсов, управляющие транзакцией для ресурсов, принимают участие в транзакции. Во время подготовительной фазы каждый диспетчер ресурсов может определять исход транзакции. Эта фаза стартует, когда инициатор транзакции посылает подтверждение для того, чтобы завершить транзакцию. Диспетчер транзакции посылает сообщение Prepare (подготовиться) всем диспетчерам ресурсов. Если диспетчер ресурсов может выполнить свою работу для успешного исхода транзакции, он посылает сообщение Prepared (готов) диспетчеру транзакций. Диспетчер ресурсов может прервать транзакцию, если он не может подготовиться к своей работе, заставив диспетчер транзакций выполнить откат. После того, как сообщение Prepared отправлено, диспетчер ресурсов должен гарантировать успешное завершение работы на фазе фиксации. Чтобы это было возможно, устойчивые диспетчеры ресурсов должны писать журнал, внося в него информацию о состоянии готовности, чтобы иметь возможность продолжить работу с этого места, например, в случае отключения питания между фазами подготовки и фиксации. Фаза фиксации начинается, когда все диспетчеры ресурсов успешно подготовились к работе, а именно - когда получено сообщение Prepared от всех участвующих диспетчеров ресурсов. Затем диспетчер транзакции может завершить работу транзакции и вернуть сообщение Commited.

До того, как в библиотеке классов .NET Framework появилось пространство имен System.Transaction, приходилось создавать транзакции традиционными способами непосредственно в ADO.NET либо реализовывать их с помощью компонентов, атрибутов и исполняющей системы COM+, которые содержались в пространстве имен System.EnterpriseServices.

При использовании традиционных транзакций ADO.NET если не создавать транзакцию вручную, то возможна единственная транзакция с каждым SQL-оператором. Если несколько операторов должны участвовать в одной и той же транзакции, придется создавать транзакцию вручную. Для создания соединения используется класс SqlConnection, который определяет метод BeginTransaction(), возвращающий после выполнения объект транзакции SqlTransaction. Этот объект затем должен быть ассоциирован с каждой командой, участвующей в транзакции. Чтобы ассоциировать команду с транзакцией, необходимо установить свойство Transaction класса SqlCommand в созданный экземпляр SqlTransaction. Таким образом, если имеется несколько команд, которые должны выполняться в одной транзакции, то каждая из них должна быть ассоциирована с транзакцией. Как каждая транзакция ассоциирована с соединением, так и каждая из этих команд должна быть ассоциирована с тем же экземпляром соединения; локальная транзакция всегда ассоциирована с одним соединением. В случае возникновения ошибок перечисленными классами генерируются исключения, которые могут быть отслежены и обработаны с помощью конструкции try/catch, где обработка заключается в вызове метода Rollback() транзакции для выполнения отката. Если создать объектную модель постоянного хранения, используя множество объектов, которые должны существовать внутри одной транзакции, то в этом случае становится очень трудно использовать транзакции ADO.NET. Здесь необходимо передавать транзакцию всем объектам, принимающим участие в этой транзакции. Кроме того, транзакции ADO.NET не являются распределенными. В транзакциях ADO.NET трудно заставить работать различные объекты в пределах одной и той же транзакции.

Пространство имен System.EnterpriseServices содержит множество полезных служб, одна из которых - автоматические транзакции. Использование транзакции с System.EnterpriseServices обладает тем преимуществом, что при этом не приходится иметь дело с транзакциями непосредственно; транзакции автоматически создаются исполняющей средой. Все, что необходимо сделать - это просто добавить атрибут [Transaction] с транзакционными требованиями в класс, а атрибутом [Autocomplete] - пометить метод для автоматической установки бита состояния транзакции: если метод успешен, устанавливается бит успеха, так что транзакция может быть зафиксирована. В случае исключения транзакция автоматически откатывается. Огромное преимущество создания транзакций с помощью System.EnterpriseServices состоит в том, что множество объектов могут быть легко запущены в одной и той же транзакции, и транзакции очень легко использовать. Недостатком же является то, что требуется модель хостинга COM + , и то, что класс, использующий средства этой технологии, должен наследоваться от базового класса ServicedComponent.

Пространство имен System.Transactions, доступное, начиная с версии .NET 2.0, привнесло новую транзакционную модель в приложения .NET. Transaction - базовый класс для всех транзакционных классов, определяет свойства, методы и события, доступные во всех транзакционных классах. CommittableTransaction - единственный транзакционный класс, поддерживающий фиксацию. У этого класса есть метод Commit(), все прочие транзакционные классы могут только выполнять откат. Класс DependentTransaction используется с транзакциями, зависящими от других транзакций. Зависимая транзакция может зависеть от транзакции, созданной внутри другой фиксируемой транзакции. Затем зависимая транзакция добавляет свое действие к исходу фиксируемой транзакции, если она успешна или нет. Класс SubordinateTransaction применяется в сочетании с координатором распределенных транзакций (Distributed Transaction Coordinator - DTC). Этот класс представляет транзакцию, не являющуюся корневой, но управляемой DTC.

Класс Transaction не может быть зафиксирован (commit) программно; он не имеет метода для фиксации транзакции и поддерживает только прерывание транзакции. Единственный транзакционный класс, поддерживающий фиксацию, - это CommitableTransaction. В ADO.NET транзакция может быть получена вместе с соединением. Объект одного из трех классов-наследников System.Transactions.Transaction может быть задействован с соединением посредством вызова метода EnlistTransaction класса SqlConnection, таким образом, соединение ADO.NET ассоциируется с транзакцией..Transactions, как и SQL Server 2005, поддерживает распространяемые транзакции. В зависимости от ресурсов, принимающих участие в транзакции, создается локальная или распределенная транзакция. С ресурсами, не поддерживающими распространяемые транзакции, создаются транзакции распределенные. Если множество ресурсов добавляется к единственной транзакции, то такая транзакция может начинаться как локальная и при необходимости превращаться в распределенную.

Подобное превращение происходит, когда к одной транзакции добавляется множество соединений с базой данных SQL Server 2005. Транзакция стартует, как локальная и затем превращается в распределенную, это отражает распределенный идентификатор, который приобретает значение, отличное от нуля. Распространение транзакции требует запуска координатора распределенных транзакций (DTC).

В случае зависимых транзакций появляется возможность влиять на одну транзакцию из множества потоков. Зависимая транзакция зависит от некоторой другой транзакции и влияет на ее исход. Зависимая транзакция DependantTransaction может определять исход транзакции, вызвав либо метод Complete(), либо метод Rollback(). Метод Complete() устанавливает бит успеха. Если корневая транзакция завершается, и все зависимые транзакции имеют бит успеха, установленный в true, то транзакция фиксируется. Если любая из зависимых транзакция устанавливает бит прерывания, вызывая Rollback(), то и вся корневая транзакция отменяется. Запустить зависимую транзакцию можно следующим образом: в начале создается объект CommitableTransaction, затем вызывается метод DependentClone() этого объекта, который возвращает объект DependantTransaction. Метод DependentClone() требует аргумента типа DependentCloneOption, который представляет собой перечисление, состоящее из двух возможных значений: BlockCommitUntilComplete и RollbackIfNotComplete. Эта опция важна, если корневая транзакция завершается перед зависимой транзакцией. Установив данную опцию в RollbackIfNotComplete, транзакция прерывается, если зависимая транзакция не вызывает метод Complete() перед вызовом Commit() корневой транзакции. Установив опцию BlockCommitUntilComplete, метод Commit() ожидает, пока не будет ясен исход всех зависимых транзакций.

Наибольшим преимуществом System.Transactions является средство включающих (ambient) транзакций. При использовании включающих транзакций нет необходимости вручную связывать соединение с транзакцией; это делается автоматически из включающих транзакций, поддерживающих ресурсы. Включающая транзакция ассоциируется с текущим потоком. Получать и устанавливать включающую транзакцию можно через статическое свойство Transaction.Current. API-интерфейсы, поддерживающие включающие транзакции, проверяют это свойство, чтобы получить такую транзакцию и соединить с текущей локальной транзакцией. Соединения ADO.NET поддерживают включающие транзакции. Таким образом, можно создать объект CommittableTransaction и присвоить его свойству Transaction.Current для инициализации включающей транзакции. Другой способ создания такой транзакции состоит в применении класса TransactionScope. Конструктор TransactionScope создает включающую транзакцию. Поскольку реализован интерфейс IDisposable, можно легко использовать область действия транзакции, применив оператор using. Конструктор по умолчанию создает новую транзакцию. Немедленно после создания экземпляра TransactionScope транзакция ассоциируется со средством доступа get свойства Transaction.Current для отображения информации о транзакции на консоли. Поскольку класс SqlConnection поддерживает включающие транзакции, он автоматически связывает их с соединением. Однако если соединение ADO.NET не должно связываться с включающей транзакцией, можно установить значение Enlist=false в строке соединения.

Используя класс TransactionScope, можно вкладывать области действия друг в друга. Вложенная область может располагаться непосредственно внутри другой области или внутри метода, вызванного из этой области. Вложенная область может использовать ту же транзакцию, что и внешняя область, подавлять транзакцию либо создавать новую транзакцию, независимую от внешней области. Требование для области действия заключается в том, что она должна быть определена перечислением TransactionScopeOption, передаваемым конструктору класса TransactionScope. Перечисление имеет следующие значения:

·        Required - определяет, что область действия требует транзакции. Если внешняя область (контекст) уже содержит включающую транзакцию, то внутренняя область использует эту существующую транзакцию. Если же включающая транзакция не существует, то создается новая;

·        RequiresNew - всегда создает новую транзакцию. Если во внешнем контексте уже определена транзакция, то транзакция из вложенной области действия полностью независима. Обе транзакции могут фиксироваться или отменяться независимо друг от друга;

·        Suppress - область действия не содержит включающей транзакции, независимо от того, содержит внешняя область транзакцию или нет.

Если несколько потоков должны использовать одну и ту же включающую транзакцию, нельзя просто создать транзакционный контекст и внутри него создать новый поток. Поскольку включающие транзакции привязаны к потоку, вновь созданный поток не имеет включающей транзакции. Однако если это необходимо, можно создать зависимую транзакцию, которая будет зависеть от включающей транзакции. Внутри нового потока можно назначить зависимую транзакцию включающей транзакции.

Из всего набора свойств транзакции ACID изоляция требуется не всегда. Из соображений производительности можно снизить степень требуемой изоляции, но при этом нужно представлять себе возможные последствия изменения уровня изоляции. Проблемы, с которыми можно столкнуться, когда не полностью изолирован контекст извне транзакции, делятся на три описанных ниже категории.

·        Грязное чтение. При грязном чтении другая транзакция может читать записи, измененные внутри данной транзакции. Поскольку данные, изменяемые внутри транзакции, могут быть откатаны к своему исходному состоянию, чтение такого промежуточного состояния из другой транзакции трактуется как «грязное», то есть данные не были зафиксированы. Этого можно избежать блокировкой изменяемых записей;

·        Невоспроизводимое чтение. Невоспроизводимое чтение случается, когда данные читаются внутри транзакции, и пока эта транзакция выполняется, другая транзакция изменяет те же самые записи. Если запись читается повторно внутри транзакции, то получается отличающийся результат - невоспроизводимый. Это можно предотвратить посредством блокировки чтения записей;

·        Фантомное чтение. Фантомное чтение случается при чтении диапазона данных, например, с использованием конструкции WHERE. Другая транзакция может добавить новую запись, которая попадает в диапазон подлежащих чтению в транзакции. Новая запись с тем же выражением WHERE возвратит другое количество строк. Фантомное чтение может стать серьезной проблемой при выполнении оператора UPDATE для диапазона записей. Например, UPDATE Addresses SET Zip=4711 WHERE (Zip=2315) обновляет почтовый код во всех записях с 2315 на 4711. Однако после выполнения обновления в таблице могут остаться записи со значением почтового кода 2315, если другой пользователь добавит новую запись с кодом 2315 в то время, пока выполняется обновление. Этой проблемы можно избежать, если применить блокировку диапазона.

При определении требований изоляции можно установить уровень изоляции. Уровень изоляции устанавливается перечислением IsolationLevel, которое конфигурируется при создании транзакции (либо конструктором класса CommitedTransaction, либо конструктором класса TransactionScope). IsolationLevel определяет поведение блокировки. Перечисление содержит следующие значения:

·        ReadUncommitted. С ReadUncommitted транзакции не изолированы друг друга. Этот уровень не предполагает ожидания блокированных записей из других транзакций. При этом незафиксированные данные могут быть прочитаны из других транзакций, то есть имеет место грязное чтение. Этот уровень обычно используется для чтения тех записей, для которых не страшно внесение промежуточных изменений, например, для отчетов;

·        ReadCommitted. ReadCommitted ожидает записей, заблокированных по записи из других транзакций. При этом не может произойти никакого грязного чтения. Этот уровень устанавливает блокировку чтения текущей записи и блокировку записи тех, что должны быть записаны до завершения транзакции. Поскольку при чтении каждая запись разблокируется при переходе к следующей, при этом может случиться невоспроизводимое чтение;

·        RepeatableRead. RepeatableRead удерживает блокировку чтения записей до тех пор, пока не завершится транзакция. При этом снимается проблема невоспроизводимого чтения. Однако все-таки могут возникнуть фантомные записи;

·        Serializable. Serializable удерживает блокировку диапазона. Пока исполняется транзакция, невозможно добавить новую запись, относящуюся к тому же диапазону, из которого прочитаны данные;

·        Snapshot. Уровень изоляции Snapshot - единственно возможный в SQL Server 2005. Этот уровень ограничивает блокировки, поскольку модифицируемые записи копируются. Таким образом, другие транзакции все же могут читать старые данные без необходимости ожидания разблокировки;

·        Unspecified. Уровень Unspecified указывает, что поставщик использует другое значение уровня блокировки;

·        Chaos. Уровень Chaos подобен ReadUncommitted, но в дополнение к действиям, характерным для ReadUncommitted, Chaos не блокирует обновляемые записи.

Уровень изоляции в классе TransactionScope может быть установлен с помощью конструктора, в котором можно установить, кроме всего прочего, TransactionOptions. Класс TransactionOptions позволяет определить уровень изоляции (IsolationLevel) и таймаут (Timeout).

.1.4 Средства удаленного взаимодействия с объектами

Проблема связывания отдельных частей распределенных приложений существует почти столько же лет, сколько и сама вычислительная техника. Удаленный вызов процедур (Remote Procedure Call, RPC) остается важной технологией, используемой для «ручного» связывания компонентов распределенных приложений. Она опирается на синхронный режим взаимодействия между двумя прикладными модулями (клиентом и сервером), т. е. работа клиента блокируется до окончания обработки задания на сервере.

Для установки связи, передачи вызова и возврата результата клиент и сервер используют специальные программные «заглушки» (client stub и server stub). Эти процедуры не реализуют никакой прикладной логики, а лишь изолируют прикладные модули от уровня сетевых коммуникаций. Обычно описания этих stub-процедур генерируются средой разработки. И клиентская, и серверная программа могут вызывать их как обычные локально-исполняемые функции. Правда, для установления соединения необходимо проделать ряд дополнительных операций, как со стороны клиента, так и со стороны оболочки исполнения. Эти операции довольно типичны для всех подобных технологий, и о них будет сказано позднее.

В общем случае механизм RPC создает статические отношения между компонентами распределенного приложения: привязка клиентского процесса к конкретным серверным суррогатам происходит на этапе компиляции и сборки и не может быть изменена во время выполнения. Этим RPC невыгодно отличается от систем, ориентированных на обмен сообщениями (Message-Oriented Middleware, МОМ), позволяющих динамически выбирать сервер, или некоторых мониторов транзакций, поддерживающих возможности оптимального распределения нагрузки на серверы и средства восстановления при сбоях.очень широко используется в разных других технологиях. Например, на RPC базируется коммуникационный слой модели Windows COM/DCOM. Основной недостаток RPC-систем заключается в синхронности взаимодействия, из которого вытекает необходимость установления стабильного соединения между взаимодействующими приложениями (в принципе есть надстройки над RPC, эмулирующие асинхронность, но они не дают и десятой доли преимуществ МОМ). В итоге распределенные системы оказываются чувствительны к надежности и доступности каналов связи.

Компонентная модель COM (Component Object Model) является наследником средств динамического связывания приложений DDE (Dynamic Data Exchange), имевшихся еще в самых первых версиях Microsoft Windows. Она позволяет разбить приложение, работающее на отдельном ПК, на компоненты, характеризуемые четко описанными интерфейсами. Так как в таком компоненте пользователю доступен только интерфейс, то при его сохранении, сохраняется и связанность компонентного приложения, даже если отдельные компоненты заменяются на другие или, скажем, переписываются на другой язык программирования. Конечно, для сохранения работоспособности приложения в целом необходимо, чтобы каждый такой компонент вел себя в точности, как его предшественник.

Вариантом COM, позволяющим строить распределенные приложения, - является DCOM (Distributed Component Object Model). Она распространяет принципы вызова удаленных процедур на объектные приложения COM. В DCOM взаимодействие удаленных объектов базируется на спецификации Distributed Computing Environment Remote Procedure Call (DCE RPC). Среда скрывает от клиента детали сетевого взаимодействия. DCOM в отличие от RPC позволяет динамически связывать удаленные объекты: клиент может обратиться к серверу-объекту на фазе исполнения, даже если не располагает на этапе компиляции информацией об его свойствах. В частности, имеется возможность нахождения объектов на сервере. Информацию об объектах, доступных на сервере на этапе исполнения, клиент получает из специального хранилища метаданных об объектах (Type Library), используя механизм OLE Automation. За счет этого можно менять функциональность серверов, не внося существенных изменений в код клиентских компонентов программы. В модели DCOM допускается использовать созданную Microsoft модификацию языка описания интерфейсов IDL (Interface Definition Language) - DCE IDL, но он не играет важной роли и служит в основном для удобства описания объектов. Реальная интеграция объектов в DCOM происходит не на уровне абстрактных интерфейсов, а на уровне двоичного кода.

В Windows есть возможность группировать объекты DCOM в рамках транзакций. За это отвечает служба ОС Microsoft Transaction Server (MTS). Он предлагает все стандартные функции монитора транзакций. Важным свойством MTS является то, что свойства транзакционности задаются на уровне контейнера COM. (Кроме того, в Windows входит служба Distributed Transaction Service, отвечающая за координацию распределенных транзакций в базах данных.) MTS был одной из первых коммерческих систем, комбинировавших поддержку транзакций и компонентов. MTS лучше всего описывать как контейнер для транзакционных компонентов или сервер приложений. Приложения, управляемые MTS, представляют из себя наборы COM-компонентов, оформленных в виде динамически подключаемых DLL-библиотек (Dynamic Link Libraries). С технической точки зрения есть несколько сервисов ОС, контролирующих работу инициированных компонентов COM и отвечающих на исходящие от этих компонентов вызовы. Данные сервисы и обеспечивают автоматическую поддержку транзакций, обеспечение безопасности, образование пулов соединений к БД, поддержку потоков выполнения и управление состоянием объектов.

Среда DCOM/MTS удобна для создания распределенных приложений, но она намертво привязана к платформе Windows, что не всегда приемлемо. Важные проблемы, возникающие при построении Windows-приложений, - проблемы надежности, производительности и контроля версий используемых библиотек компонентов (и связанных с ними библиотек DLL).

Стоит заметить, что в связи с переходом на платформу Microsoft .Net для построения решений корпоративного класса технологию DCOM стоит рассматривать как унаследованную и учитывать лишь с точки зрения интеграции в новые корпоративные информационные системы большого числа написанных на ней приложений.

В середине 90-х годов прошлого столетия для построения распределенных приложений стала применяться технология CORBA (Common Object Request Broker Architecture - общая архитектура брокера объектных запросов). Она разделяет много идей, заложенных в COM и RPC, но использует их особым образом. Ключевым компонентом архитектуры CORBA является язык IDL, на котором описываются интерфейсы вызываемых серверных компонентов. На его базе компиляторами генерируются классы-«заглушки» (stubs) и «посредники» (proxy). Они задействуются соответственно программами на клиенте и сервере как локальные объекты, но на самом деле отвечают за посредничество в передаче обращений к удаленной системе. С их помощью обеспечивается также независимость от конкретного объектно-ориентированного языка. Для управления серверными объектами применяется брокер запросов (Object Request Broker, ORB). В числе прочего он позволяет клиенту получать сведения о доступных объектах и их свойствах. В отличие от DCE IDL, язык CORBA IDL поддерживает основные понятия объектно-ориентированной парадигмы (инкапсуляцию, полиморфизм и наследование). Есть также возможность динамического связывания удаленных объектов - ее реализует интерфейс динамического вызова DII (Dynamic Invocation Interface). Однако поддержка этого интерфейса - опциональна и зависит от реализации ORB.

Для CORBA-взаимодействий используется специальный протокол IIOP (Internet InterORB Protocol). Вся механика сетевых и объектных коммуникаций от клиента скрывается - брокер автоматизирует процессы передачи свойств объектов (marshaling/unmarshaling), технические сложности установления соединения и т.п. Однако использование специального протокола требует некоторых усилий по организации его транспорта через брандмауэры на границе корпоративной сети.изначально была нацелена на кросс-платформенную поддержку. Ее реализации имеются для всех разновидностей Unix, Windows и многих других популярных ОС. В этом ее главное преимущество перед объектной моделью Microsoft. Но часто эти реализации сильно различаются, т.е. они не до конца интероперабельны. Стандарт определяет почти два десятка служб брокера запросов, из которых часто реализуется в брокерах лишь несколько ключевых. В числе наиболее известных на сегодня продуктов VisiBroker и Enterprise Server компании Borland, BEA Tuxedo и IONA Orbix.

Механизм динамического вызова CORBA сложен в реализации. А ввиду недостаточной интегрированности с традиционными средствами разработки он долго не мог получить широкого признания, на которое претендовали его создатели. Кроме того, ORB-системы, как и RPC, ориентированы на синхронную связь. На сегодняшний день CORBA имеет большое унаследованное значение, но новые распределенные приложения чаще разрабатываются на J2EE и Microsoft .NET.

В язык программирования и платформу Java изначально встраивались средства для синхронного удаленного вызова методов RMI (Remote Method Invocation), очень похожие на CORBA. Как и в CORBA, в RMI используются свои «заглушки» и «каркасы» (описания интерфейсов объектов), а так же брокер запросов RMI. Упрощенная последовательность шагов для связывания модулей программы выглядит примерно следующим образом. Сначала необходимо определить интерфейс дистанционно вызываемого объекта как производный от стандартного Java-интерфейса java.rmi.RemoteException, а затем реализовать методы этого интерфейса в нужном объекте. Затем на основе интерфейса удаленного доступа сгенерировать RMI-заглушку для клиента. Во время запуска серверного приложения необходимо зарегистрировать класс на сервере имен. Клиент с помощью службы имен JNDI получает ссылку на экземпляр удаленного объекта и преобразует ее к типу интерфейса удаленного объекта. Затем клиент обращается с полученным интерфейсным объектом, как с локальным. Класс-посредник должен при этом присутствовать в пакете клиентского приложения, т.к. он отвечает за преобразование вызовов и отсылку пакетов на удаленный сервер. В роли параметров вызываемых на клиенте методов могут выступать Java-объекты. Поддерживается передача как по ссылке, так и по значению. Посредник и RMI-сервер скрывают от программиста сложности сериализации и десериализации объектов-параметров. В этом смысле RMI аналогичен Microsoft .Net Remoting. В принципе, CORBA 2.3 также поддерживает данные механизмы, но использовать функционал CORBA гораздо сложнее. Еще одно сходство RMI и .NET Remoting состоит в возможности активизации по запросу клиента объекта, уже давно существующего на сервере. Для связи RMI использует собственный коммуникационный протокол JRMP. Обычно он работает поверх транспортного протокола TCP, но могут быть исключения, например сервер BEA WebLogic применяет фирменный протокол T3.

Важно отметить сближение CORBA и RMI. Брокер, удовлетворяющий стандарту CORBA 2.3.1, является частью Java 2 Platform, Standard Edition, v1.3, причем он тоже написан на Java, и потому является переносимым. Он может также взаимодействовать с другими CORBA 2.3.1-совместимыми брокерами. Но главное, брокер поддерживает одновременно и вызовы компонентов по протоколу IIOP, и вызовы по RMI. При этом можно организовать взаимодействие между двумя Java/RMI-платформами через IIOP, что часто предпочтительно с точки зрения туннелирования трафика через брандмауэр. Пользователь Java также избавлен от необходимости делать какие-либо дополнительные лицензионные платежи для использования CORBA. Поддержка CORBA есть и в J2EE, а стало быть, в серверах Web-приложений корпоративного класса, например WebLogic 8.1. Используя функцию RMI-IIOP, программист может при помощи RMI API обратиться к CORBA-северу. Это позволяет вызывать из компонента Enterprise Java Beans (EJB) удаленные объекты, написанные как на Java, так и на других CORBA-совместимых языках. Можно также вызывать объекты EJB из сторонних программ, воспользовавшись сервисами CORBA. Для этого необходимо сгенерировать их IDL-интерфейсы и интерфейсы-посредники.WebLogic предлагает еще одну новую технологию - динамические объекты-посредники, которые позволяют устранить необходимость генерации классов посредников на этапе разработки приложения. Они представляют собой сериализуемые классы, передаваемые через сеть клиенту, когда тот находит нужный объект путем запроса к RMI-реестру. Как и в случае со статическими proxy, клиент работает с ними, как с обычными локальными объектами. Посредники же сериализуют запросы клиента и переправляют их в WebLogic Server. Остается проблема, когда и как генерировать подобные динамические прокси-классы. Сервер Weblogic позволяет делать это во время исполнения, когда происходит регистрация удаленного объекта. Компилятор «на лету» выявляет реализованные классом интерфейсы, определенные как производные от интерфейса дистанционного доступа, и продуцирует весь необходимый дополнительный байт-код. Он также создает вспомогательный серверный байт-код, отвечающий за десериализацию запросов клиента и передачу их на исполнение методам реального объекта, а также сериализацию результатов и возврат их обратно посреднику клиента. Иначе говоря, все, что нужно для создания приложения, - это просто зарегистрировать в реестре объект, реализующий дистанционно доступный интерфейс. Надо сказать, что несмотря на подобные ухищрения, технология RMI оказалась слишком низкоуровневой и сложной для непосредственного построения на ее базе распределенных приложений и де-факто была скрыта Java-технологией корпоративных объектов Enterprise Java Beans (EJB), а именно их контейнерами.

Вторая важная технология связывания Java-приложений называется Java Messaging Service (JMS). Она предлагает обобщенную модель и набор интерфейсов для средств гарантированной доставки сообщений. JMS позволяет связать компоненты, работающие под управлением разных Java-машин асинхронным образом. Как правило, в качестве провайдера функций JMS служат какие-либо из существующих платформ MOM, например WebSphere MQ корпорации IBM или JES Message Queue Enterprise корпорации Sun Microsystems. Фактически она является модификацией технологии MOM, о которой речь пойдет ниже, в применении к платформе Java. Спецификация JMS обеспечивает универсальный Java API, позволяющий разработчикам писать приложения, не заботясь о том, какое конкретно транспортное ПО будет использоваться. JMS позволяет передать Java-объект целиком, не думая об его преобразовании в подходящий для передачи формат (средства сериализации). Но при этом она только определяет порядок доступа к корпоративной системе обмена сообщениями, а каждый производитель JMS-ПО самостоятельно разрабатывает инструменты для администрирования среды подобного обмена. Благодаря асинхронности JMS обеспечивает существенно большую независимость от обрывов канала связи, а также масштабируемость системы. В последних спецификациях появились требования к реализациям по распределению нагрузки, отказоустойчивости, обработки ошибок. JMS включен в J2EE как стандартный компонент начиная с версии 1.3. Современные реализации JMS опираются на понятие фабрик соединений и конечных адресатов. Имеющиеся средства администрирования позволяют привязать эти объекты к системе имен Java Naming Directory Interface (JNDI). Клиент может сделать запрос к серверу JNDI и получить по осмысленным для человека именам ссылки на фабрику соединений и пункт назначения сообщения. С помощью этих ссылок он создает сначала соединение, а затем и сессию связи с конкретным адресатом и начинает отправлять ему сообщения. Поддерживаются также и неявные схемы доставки - публикации - подписки, которые характерны для брокеров сообщений. Спецификация требует от провайдеров обеспечения поддержки многих функций, включая гарантированную доставку, временное хранение сообщений на постоянном носителе, отсылки групп сообщений (локальная транзакционность) и прочее. И эти их качества становятся типовыми. Однако большой люфт в методах реализации спецификации остается - например, приоритет обслуживания лишь предлагается учитывать и он может и не поддерживаться менеджером очередей. Аналогичным образом указание времени жизни сообщения, указываемое при его отправке, не является командой для менеджера очередей, и клиенту не запрещается принять устаревшее сообщение. В заключение стоит сказать, что основной трудностью при работе с Java-ориентированными продуктами естественно является требование использования языка Java и связанной с ним платформы исполнения байт-кода (виртуальной машины), что не всегда желательно..NET Remoting - новейшая технология связывания распределенных приложений. Она обеспечивает вызов методов удаленных объектов, находящихся под управлением среды Microsoft .NET. Поддерживается множество услуг, таких, как активация удаленных объектов, контроль времени их существования, взаимодействие между удаленными объектами через различные транспортные каналы. Во многом .NET опирается на тот опыт, что был накоплен за много лет создания распределенных приложений на базе RPC, CORBA, DCOM и EJB. В архитектуре .NET Remoting также выделяют клиентскую и серверную машину. Сервером является хост, отвечающий на запрос, а клиентом - хост, его отправляющий. Однако в отличие, скажем, от EJB это разделение относительно условно - в любой момент роли могут быть изменены. В этом смысле она похожа на технологию Web-сервисов, которая отдельно рассматривается ниже. Как и в RPC, и CORBA, клиент локально работает только с классом-посредником (proxy). Этот класс генерируется средой одним из трех способов: либо на основе экземпляра кода удаленного класса, либо на основе класса, содержащего описания интерфейсов удаленного класса (но не код его логики), либо специальным инструментом на основе получаемого с дистанционного сервера описания Web-сервиса. Вкратце схема взаимодействия выглядит следующим образом: в сообщении, отсылаемом клиентом серверу, классы-посредники кодируют название вызываемого метода удаленного объекта и все входящие переменные. На сервере сообщение раскодируется (т.е. определяется название метода и все входящие параметры) и выполняется реальный метод. Чтобы передать какие-либо данные на сервер, достаточно вызвать метод класса-посредника в качестве аргументов метода, взяв эти данные. Например, можно передавать любые двоичные данные или строки текста. Сервер, в свою очередь, аналогичным образом может передать клиенту возвращаемое значение метода - оно будет раскодировано на клиенте через класс-посредник. В этом отношении .NET Remoting напоминает сериализацию Java-объектов, применяемую, в частности, в JMS.

Отличительной особенностью .NET Remoting является наличие нескольких четко разделенных слоев, отвечающих за сокрытие от пользователя сложностей дистанционного взаимодействия с объектом. Первым из них является слой «прозрачных» посредников (TransparentProxy), отвечающих за упаковку всех параметров удаленного вызова в объект-посылку IMessage. Сформированная посылка передается в «настоящий» посредник (RealProxy), который и передает ее на удаленный сервер. Все эти классы заранее заданы средой .NET, но пользователь может расширять класс RealProxy, например, для подключения своих средств безопасности. Для взаимодействия с коммуникационным слоем RealProxy опирается на каналы. В канале важны два элемента - формовщик (formatters) и транспортировщик (transport sink). Формовщик отвечает за преобразование сообщения IMessage в пакеты данных протокола высокого уровня. В стандартном варианте .NET содержится два формовщика: двоичный и SOAP. Транспортировщик решает проблему доставки этих пакетов на более низком сетевом уровне - для стандартной редакции .NET это TCP и SOAP. Таким образом, доступны два стандартных вида взаимодействия (пользователем могут быть описаны и иные): для взаимодействия по двоичному каналу через TCP и пересылки запросов к удаленным объектам в виде сообщений SOAP поверх HTTP. В первом случае достигается наибольшая производительность, но ценой утраты значительной части кросс-платформенности. Он подходит для построения приложений в локальных сетях. Во втором случае методы удаленного объекта видны как Web-сервисы, обращение к которым может происходить через Интернет и границу, определенную брандмауэром. При этом может использоваться любой транспортный протокол третьего уровня модели OSI, а не только IP, как в первом случае. Однако для его реализации требуются большие вычислительные и сетевые ресурсы, нежели для случая двоичной связи. Протокол, который обращается к серверному приложению для доступа к своим объектам, настраивается при его запуске. Сначала регистрируется канал связи, затем как подключенные к нему регистрируются доступные дистанционные объекты. При регистрации канала указывается транспортный протокол (TCP, HTTP и пр.), протокол доступа (двоичный, SOAP и другие) и номер порта, по которому сервер будет принимать запросы. В .NET различаются два типа объектов - активизируемые сервером и клиентом. Объекты, активизируемые клиентом, находятся под управлением менеджера контроля жизненного цикла, гарантирующего, что по истечении определенного срока действия объекта (срока аренды, в терминологии Microsoft) будет произведена необходимая очистка памяти. Среда предоставляет развитые средства для контроля за этой арендой, в том числе и со стороны самого объекта. Объекты, активизируемые сервером, делятся на объекты для однократного обращения (Single call) и «единичные» (Singleton). Экземпляры однократных объектов создаются в момент обращения к их методам, «единичные» объекты могут существовать долго и сохранять состояние между вызовами их методов. Все настройки свойств объектов (типы объектов, каналы, номера портов) можно сохранить в конфигурационном XML-файле, благодаря чему перестройка приложения становится совсем простой. Число типов объектов в .NET Remoting существенно меньше, чем в EJB. Например, отсутствуют Persistent-объекты, связанные с записями в базах данных. Но для модели вычислений, принятой в .NET, это не нужно. Нет также средств обеспечения транзакционности на уровне контейнера, наличествующих в серверах приложений EJB. Но часто подобные расширения создают лишь дополнительные сложности, не давая программистам никакой экономии сил. Например, создание и регистрация объекта EJB - очень трудоемкая вещь, а все, что нужно для создания дистанционно доступного класса .NET Remoting, - это унаследовать его от класса MarshalByRefObject. В заключение стоит отметить, что .NET Remoting совместим с иными технологиями удаленного доступа, например COM/DCOM (через средства самой Microsoft), а благодаря поддержке открытых стандартов, наподобие XML и SOAP, можно теоретически организовать взаимодействие с приложениями на других платформах, в первую очередь Java. Важной особенностью .NET Remoting является то, что она не ориентируется исключительно на синхронное взаимодействие, а при применении SOAP-транспорта настраивается и на асинхронный режим работы. Это означает, что она может быть применена в сочетании со средствами гарантированной доставки сообщений для обеспечения максимальной надежности системы в целом.

Особого внимания заслуживает технология Web-сервисов. Web-сервисы (WS) позиционируются в настоящее время как универсальная технология связывания существенно разнородных систем. В ее основе лежит несколько стандартов: XML для описания данных, SOAP для передачи информации с одних систем на другие, WSDL для описания сервисов (в том числе задания типов входных и выходных данных) и UDDI для хранения и предоставления по запросу WSDL-описаний. Для создания относительно несложных систем этих трех стандартов достаточно. Однако уже сколько-нибудь нетривиальные решения требуют использования таких вещей, как гарантированная асинхронная доставка сообщений, управление транзакциями, шифрование пересылаемых между системами данных и обеспечение их неподдельности. Все эти направления так или иначе родственны WS, и активно создается некоторая надстройка разных спецификаций, позволяющая вписать эти технологии в мир WS, наблюдается попытка сделать решение частной проблемы панацеей от всех бед, в том числе и для областей, для которых WS изначально предназначены не были.

В частности существуют транзакционные протоколы на основе WS. Концептуально они делятся на два широких класса - атомарные транзакции и бизнес-транзакции. Атомарные транзакции - это наборы операций, осуществляемые в рамках границ очень небольшого доверительного домена и имеющие свойство «все или ничего», характеризующиеся набором свойств ACID. Все действия в такой транзакции, осуществленные до операции ее утверждения, являются предварительными, т.е. не сохраняются на постоянном носителе и не видны другим процессам. Выделяют одно- и двухфазные транзакции. В первом случае имеется лишь один участник, во втором их несколько и требуется дополнительная «подготовительная фаза», когда координатор последовательно запрашивает всех ее участников, могут ли они выполнить транзакцию. Если от них всех присылается положительный ответ, то координатор утверждает все транзакции нижнего уровня. Если какая-то из транзакций не может быть выполнена, то все они отменяются. Подобный подход позволяет всем приложениям действовать скоординировано, не допуская возникновения несогласованных состояний других приложений и в то же время ничего о них не зная. Бизнес-транзакции обычно существенно отличаются по характеру от атомарных. Вкратце их особенности сводятся к следующему:

·        Ввиду большой длительности бизнес-действие может потреблять большое количество ресурсов, в частности - включать большое количество атомарных транзакций;

·        Индивидуальные задачи могут завершаться так, что их данные становятся общедоступны до завершения бизнес-транзакции в целом. Результаты исполнения этих задач могут иметь влияние на внешние компьютерные системы;

·        Ответ на пользовательский запрос может занять много времени. Действия в реальном мире - одобрение человеком, сборка, производство или доставка - могут иметь место еще до того, как послан электронный ответ;

·        В случае, когда возникшая исключительная ситуация требует, чтобы результаты действия, предшествующие текущему шагу, были логически отменены, просто прекратить работу транзакции недостаточно. Для обращения систем в исходное состояние алгоритм обработки исключительной ситуации может потребовать исполнения какой-то бизнес-логики, например компенсационных операций для каждого из проделанных шагов. Здесь стоит заметить, что автоматическая компенсация может оказаться неисполнимой, - ведь данные, зафиксированные на предыдущих шагах, уже могли использоваться другими процессами и дополнительно измениться;

·        Участники бизнес-действия могут находиться в разных доменах, где требуется явное установление доверительных отношений.

 

.1.6 Выводы

На основании проведенного анализа и обзора были сделаны следующие выводы. Из рассмотренных протоколов управления транзакциями классических СУБД у пессимистического подхода основным недостатком являются простои во время тупиков и растрата машинных ресурсов на их обнаружение и разрешение. Оптимистический подход бесполезно тратит машинные ресурсы на работу транзакций, которые позже будут анормально завершены из-за возникновения конфликтов, а также на работу на стадии проверки при завершении каждой транзакции. Экспериментальные результаты показывают, что в случае классических СУБД пессимистический подход обычно показывает лучшие результаты. Предполагается использовать этот протокол для разрабатываемой подсистемы управления транзакциями. Транзакции широко используются при работе с базами данных, но классы из пространства имен System.Transaction библиотеки классов .NET Framework позволяют выполнять транзакции с изменчивыми или находящимися в памяти объектами, такими как списки объектов. Если список поддерживает транзакции, объект добавляется или удаляется и транзакция завершается неудачей, то все операции со списком автоматически отменяются. Запись в списки, находящиеся в памяти может выполняться в той же транзакции, что и запись в базу данных, таким образом .NET Framework оказывается удобным средством для решения задачи данной работы.

Данные, с которыми работает автоматизированная система управления документами «ЕВФРАТ», хранятся в СУБД, которая поддерживает транзакции. Обращения к этим данным осуществляются посредством объектного API. При условии, что несколько транзакций не затрагивают взаимосвязанные объекты, они должны иметь возможность выполняться параллельно, что может быть достигнуто за счет использования нескольких потоков, однако API имеет свое ограничение: он не позволяет обращаться к одному объекту из разных потоков. Таким образом, нужен механизм, позволяющий реализовать многопоточную транзакционность над этим API, который будет организовывать работу потоков со своими объектами.

.2 Постановка задач

Целью данного проекта является разработка подсистемы - программного модуля, занимающегося управлением транзакциями для автоматизированной системы управления документами «ЕВФРАТ». Подсистема должна обеспечивать выполнение следующих функций:

.        Создание транзакций;

.        Выполнение операций с хранилищем и файлами в рамках транзакции;

.        Возможность фиксации или отката транзакции пользователем;

.        Автоматический откат транзакции в случае возникновения ошибки;

.        Выполнение транзакций в заданном числе потоков, распределение транзакций, по потокам для параллельной обработки;

.        Количество потоков - настраивается.

Требования к характеристикам подсистемы:

.        Планирование выполнения периодических действий во время простоя потоков, а так же не реже, чем через заданный интервал времени во время работы потоков;

.        Возможность использования технологии xNika[13] при работе с данными.

В ходе разработки должны быть удовлетворены следующие ограничения: подсистема должна быть разработана для платформы Microsoft .NET Framework версии 3.5 sp1 и выше с использованием языка высокого уровня C#.

.3 Разработка подсистемы управления транзакциями

Автоматизированная система управления документооборотом «ЕВФРАТ» - это система, обеспечивающая поддержку всего жизненного цикла работы с документами в электронном виде и позволяющая полностью реализовать технологии электронного документооборота на предприятии. Основные задачи, решаемые системой - автоматизация работы с входящей и исходящей корреспонденцией, организационно-распорядительными документами и договорами.

Жизненный цикл работы с документами в системе состоит из следующих фаз: создание, ввод, регистрация, движение, обработка, согласование и хранение. На его протяжении часто возникает необходимость совершать группы последовательных операций над документами, каждая из этих групп представляет собой логическую единицу работы с данными. Эта группа может быть выполнена целиком либо успешно, соблюдая целостность данных и независимо от других параллельно производимых операций, либо не может быть выполнена вообще, и тогда она не должна произвести никакого эффекта. Например, имеется документ, после регистрации которого должно создаться поручение. Если документ успешно зарегистрируется, но поручение по нему создать не получится, то необходимо отменить и регистрацию документа. В связи с этим основной задачей настоящей работы является создание инструментария, позволяющего оформлять любые группы операций с объектами системы в транзакции и управлять ими.

Архитектура АСУД «ЕВФРАТ» клиент-серверная: функциональная логика системы в основном сосредоточена на сервере приложений, в то время как клиентские приложения ориентированы на представление данных. Клиентское приложение обменивается пакетами с сервером через гарантированное TCP соединение. В настоящий момент в процессе выполнения функциональной части работы сервер приложений использует три фиксированных служебных потока (один поток ожидает соединения клиентов по TCP, второй обеспечивает передачу данных между сервером приложений и соединившимися клиентами, а третий периодически взаимодействует с хранилищем) и по одному потоку на каждый из активных сервисов[14].

При текущей организации потоков нет возможности обеспечить физически параллельную обработку нескольких клиентов, взаимодействующих с Хранилищем, и производительность системы не может быть заметно улучшена за счет увеличения вычислительной мощности. Задачей настоящей работы так же является предоставление возможности выполнения нескольких транзакций на сервере приложений одновременно, и предполагается, что это достижимо, например, при использовании в сервере современных многоядерных архитектур за счет предоставления нескольких потоков для выполнения транзакций. Таким образом будут улучшены показатели производительности системы.

Хранилище, используемое АСУД ЕВФРАТ, реализовано при помощи технологии xNika, состоящей из двух частей - серверной и клиентской. Серверная часть хранилища xNika реализует довольно низкий уровень абстракции, поэтому ее непосредственное использование потребовало бы написания вспомогательного кода, который реализован в клиентской библиотеке. Клиентская библиотека Хранилища xNika накладывает свои ограничения - с одной стороны не позволяет работать с одним объектом из разных потоков одновременно и имеет внутренний (уровня соединения) кэш, который не позволяет одновременно выполнять несколько независимых параллельных транзакций, но с другой стороны - позволяет работать с xml-данными сложной структуры, позволяет использовать объектно-ориентированный подход. Эти ограничения предполагается преодолеть за счет использования каждой транзакцией отдельного объекта соединения с хранилищем, тогда изменения, вносимые одной транзакцией, не будут мешать другим транзакциям.

.3.1 Предлагаемая модель подсистемы управления транзакциями

Сервер приложений АСУД «ЕВФРАТ» основан на наборе библиотек, реализующих объектно-ориентированную среду выполнения, которая в свою очередь реализует над средой хранения объекты системы документооборота, такие как документы, файлы, и вспомогательную инфраструктуру, включающую подсистемы кэширования, обработки событий и прочее. В контексте данной среды предлагается реализовать блоки управления рабочими потоками и соединениями, на основе которых реализовать подсистему управления транзакциями.

Функциональность блока управления рабочими потоками заключается в управлении параллельной обработкой запросов - элементов работы. Элемент работы - это объект, содержащий параметры задания и реализующий определенный интерфейс с методами, вызываемыми для выполнения задания. Рабочий поток - это объект, содержащий очередь элементов работы и исполняющий их в рамках потока. Блок реализует пул рабочих потоков, в которых параллельно выполняется вся фактическая деятельность. Элементы работы могут помещаться, в зависимости от необходимости, как в конкретный рабочий поток, так и в поток, выбираемый на основании его текущего состояния и уровня занятости его очередей - предпочтение отдается простаивающим потокам или потокам с самой короткой очередью. Рабочие потоки обрабатывают свои очереди постоянно, в промежутках через заданный интервал времени вызывают обработку событий простоя и о выполнении каждого элемента работы происходит соответствующее уведомление. Количество одновременно работающих рабочих потоков настраивается при инициализации пула в зависимости от характеристик средств вычислительной техники. Однако при этом нужно учитывать, что установка количества рабочих потоков больше числа процессоров на компьютере может привести к снижению суммарной производительности.

Блок управления соединениями по мере необходимости создает в рабочих потоках соединения со средой хранения. Для каждого потока существует свой набор соединений, каждое из которых используется в том потоке, в котором оно было создано. Когда соединение создается и используется, оно помечается как занятое, когда оно освобождается - помечается соответственно.

Транзакция в среде выполнения - это объект, занимающийся управлением различными ресурсами, используемыми в процессе согласованного изменения данных. Один из этих ресурсов - рабочий поток, который выделяется для каждой транзакции и затем закрепляется за ней. Один рабочий поток может быть отведен нескольким транзакциям, но все действия, выполняемые в рамках транзакции, выполняются в одном рабочем потоке. Другие ресурсы - соединение с хранилищем xNika и временные файлы, которые были запрошены или загружены в процессе транзакции. Для обеспечения возможности одновременной обработки нескольких транзакций, подсистема управления транзакциями использует пул подключений независимых xNika таким образом, что каждая открытая транзакция использует свой экземпляр подключения. Это необходимо для того, чтобы внутренний кэш хранилища xNika был независимый в каждой транзакции и изменения, вносимые в кэш в процессе обработки одной транзакции, не мешали работе других транзакций[13,15,16]. При завершении транзакции вызывается фиксация соответствующего ей соединения, после чего происходит очистка КЭШа, соединение приобретает статус свободного и доступного для использования последующими транзакциями.

Для исключения «зависания» открытых и незавершенных транзакций задается максимальный срок простоя транзакции - время, в течение которого допускается отсутствие какой-либо деятельности в ее рамках. По истечении такого таймаута транзакция принудительно завершается: осуществляется ее откат.

Каждая транзакция имеет очередь элементов работы, аналогичную очереди рабочего потока. Элементы из этой очереди по одному попадают в очередь конкретного потока, фиксированного за транзакцией. После завершения выполнения очередного элемента, за исключением случая, когда элемент неудачно пытался выполнить блокировку ресурсов, происходит перенос следующего элемента работы транзакции в очередь ее потока для выполнения. Иначе, в случае безуспешной попытки блокировки, блокирующий элемент заново ставится в очередь рабочего потока. Если в процессе выполнения задания, в том числе фиксации, возникает ошибка, транзакция принудительно откатывается и генерируется исключение. Автоматический откат так же происходит при превышении транзакцией лимита работы в 1 час. Все действия, производимые транзакцией, включая фиксацию, откат и освобождение ресурсов, вызываемое в любом случае при ее завершении, происходят в отведенном ей рабочем потоке.


.3.2 Модель производительности подсистемы

Предположим, имеется система с многоядерным процессором с количеством ядер равным n. Имеется некоторый набор из N операций, который должен быть выполнен. S операций из этого набора должны выполняться строго последовательно, в то время как оставшиеся P операций программно независимы друг от друга и могут выполняться параллельно на всех ядрах процессора. Пусть s = S/N - доля операций, выполняемых последовательно, а p (равное 1 - s) - доля операций, выполняемых параллельно. В случае применения одноядерного процессора время, требуемое для выполнения заданного набора операций, составляет:

(2.1)

Где IPS (Instructions Per Second) - количество операций, выполняемое в секунду. В случае использования n-ядерного процессора время, затрачиваемое на выполнение всего набора операций, составит:

(2.2)

Поскольку приростом производительности в данном случае можно считать уменьшение времени выполнения набора операций при использовании многоядерного процессора по отношению ко времени выполнения того же набора с использованием одноядерного процессора, прирост производительности составит:

(2.3)

Предлагаемая организация подсистемы управления транзакциями позволяет увеличить долю операций p, которые смогут выполниться одновременно, за счет постоянной занятости потоков и предпочтения транзакций, которым либо не нужна блокировка, либо ее успешно удается получить.

.3.3 Практическая реализация подсистемы

Объектно-ориентированная среда выполнения как объект, создаваемый единожды при запуске сервера и существующий на протяжении всей его работы, является экземпляром класса EnvironmentServer. Конструктор класса EnvironmentServer принимает на вход два параметра - строка подключения к серверу БД и количество инициируемых потоков для обработки. В конструкторе создаются и сохраняются в памяти три других объекта: пул соединений - экземпляр ConnectionPool, пул потоков - экземпляр ThreadPool и менеджер блокировок BlockManager. Последний из объектов затем доступен через соответствующее свойство EnvironmentServer. Кроме того, осуществляется подписка на событие простоя пула потоков OnIdle с целью поиска транзакций, слишком долго простаивающих и осуществление их отката, если они еще не на стадии отката или фиксации. Метод OpenTransaction() объекта EnvironmentServer создает и возвращает объект транзакции Transaction. Этот объект так же сохраняется в списке открытых транзакций EnvironmentServer и осуществляется подписка на событие завершения транзакции Complete, чтобы удалить завершенную транзакцию из списка. Метод Stop() запрещает создание новых транзакций, ожидает завершения выполняющихся, затем принудительно осуществляет их откат и вызывает останов пула потоков.

Класс ConnectionPool - управляет соединениями со средой хранения xNika. В конструкторе класса инициируется словарь-классификатор соединений xNika, содержащий информацию об открытых соединениях и их состояниях для каждого из потоков. Состояние соединения может быть одно из двух: занятое и свободное. В классе ConnectionPool определены методы GetConnection() и FreeConnection(). Метод GetConnection() возвращает свободное соединение для текущего потока. В случае отсутствия свободного соединения создается новое. Возвращаемое соединение отмечается как занятое и остается таким до вызова метода FreeConnection(). В последний передается соединение, которое помечается методом как свободное.

Класс BlockManager хранит словарь активных блокировок. Метод TryEnterBlock () получает в качестве аргумента идентификатор блокировки - текстовую строку, которая ассоциируется методом в словаре блокировок с текущей транзакцией; затем происходит подписка на событие завершения транзакции Complete с целью очистки всех блокировок, внесенных ею. Если попытка блокировки была успешной, возвращается истина, иначе возвращается ложь, если какая-то транзакция уже осуществила данную блокировку. Метод IsInLock() получает в качестве аргументов транзакцию и идентификатор блокировки и проверяет, была ли уже внесена такая блокировка, возвращая соответствующее логическое значение.

Класс ThreadPool управляет пулом рабочих потоков WorkingThread. В конструктор класса передается количество потоков и максимальный интервал между обработкой событий простоя пула потоков. На основании первого параметра конструируются и далее хранятся в памяти до останова пула экземпляры WorkingThread, представляющие собой рабочие потоки, на событие OnIdle каждого из которых осуществляется подписка с целью периодического вызова обработки события OnIdle самого пула потоков, но не чаще, чем через минимальный интервал, по умолчанию равный 100 миллисекундам, или максимальному интервалу, если он меньше 100 миллисекунд. В свою очередь цикл, выполняющийся в каждом из рабочих потоков периодически генерирует события OnIdle своего потока с интервалом, не превышающим заданное максимальное значение. Таким образом, событие простоя пула потоков возникает когда любой из рабочих потоков входит в состояние простоя, но не чаще фиксированного значения, или если с момента обработки последнего события простоя прошло времени больше, чем было указано при конструировании объекта ThreadPool; оно может быть использовано для запуска фоновых задач. Свойство Stopping позволяет идентифицировать, если пул потоков находится в состоянии останова. Метод Stop() пула потоков выполняет останов всех запущенных рабочих потоков: сначала происходит отписка от их событий OnIdle, перевод потоков в состояние завершения и блокирование добавления новых задач, затем ожидается освобождение очередей всех этих потоков, после чего потоки завершаются и их объекты уничтожаются. Метод Push() добавляет задачу, передаваемую ему в качестве аргумента, для выполнения в очередь одного из рабочих потоков. Поток выбирается тот, который не находится в состоянии останова и имеет наименее загруженный статус (наивысший приоритет) и самую короткую очередь. Попытка воспользоваться методом во время останова пула потоков приводит к исключению.

Далее рассмотрим класс WorkingThread, описывающий рабочий поток и управляющий очередью задач WorkItemsQueue в рамках потока. Конструктор создает новый поток, в котором запускается основной цикл, выполняющий задания из очереди до останова потока. Цикл выполняет задания по мере появления в очереди, вызывая во время простоя через заданный минимальный и во время работы - через заданный максимальный интервал времени между событиями простоя их обработку. Существует четыре состояния, в которых может находиться поток, они перечислены в порядке увеличения приоритета с точки зрения предпочтения потока при выборе для помещения задания в его очередь: Stopping (останов), Starting (запуск), Working (выполнение заданий из очереди) и Idle (ожидание очередного задания). Текущее состояние потока доступно для чтения через свойство State. Статическое свойство Current определяет текущий поток, если вызов производится в рамках рабочего потока, используется классом транзакции Transaction. Свойство ManagedThreadId позволяет однозначно идентифицировать поток. Свойство Context возвращает контекст текущей выполняющейся в потоке задачи, последний может использоваться ею и каждый раз очищается по завершении ее выполнения. Свойство Сount определяет общее число задач в очереди потока, а свойство queue возвращает саму очередь WorkItemsQueue. Для останова потока в классе определены методы StartStop() и Stop(): первый блокирует добавление новых заданий, а второй - сигнализирует о завершении, останавливая цикл, выполняющийся в потоке и сам поток.

Очередь потока WorkItemsQueue хранит в памяти соответствующий объект очереди и имеет метод Push(), принимающий в качестве аргумента задание WorkItem, добавляющий его в очередь и генерирующий соответствующее событие. Метод TryGet() возвращает истину и очередное задание WorkItem для выполнения, если таковое имеется в очереди, иначе - ложь, в обоих случаях генерирует соответствующие события, если очередь оказывается пуста. Метод Block() запрещает добавление заданий, а свойство Blocked определяет, является ли очередь заблокированной. Метод Wait(), принимающий на вход интервал времени, ожидает в течение этого интервала появления в очереди заданий.

Объект WorkItem представляет собой задание и конструируется с указателем на метод и его параметрами, сигнализирует об активации, запуске и завершении задания, а так же перехватывает все исключения и позволяет их обрабатывать. В конструктор класса WorkItem в качестве указателя на метод удобно передавать так называемое лямбда-выражение - сокращенное объявление анонимного метода, ставшее возможным в C# 3.0. Для объявления последнего введены два делегата anywork и anyworkandret, соответственно второй используется, если нужно возвращать результат. Метод Do() запускает выполнение задачи, а асинхронный контроль выполнения позволяет осуществлять специальный сигнализатор AsyncWaitHandle. Результат выполнения задания WorkItem доступен через свойство Result.

Кроме обычных заданий WorkItem введены так же особые ожидающие блокировку задания CLockWorkItem. Класс CLockWorkItem наследует и расширяет функциональность WorkItem. В конструктор передается объект транзакции Transaction и строковый идентификатор блокировки, далее от имени этой транзакции осуществляется попытка заблокировать ресурсы, результат которой логического типа и затем доступен через свойство Result. Метод Copy() создает заново вызвавший объект и возвращает его как WorkItem.

Основной класс, предполагающий использование, как и EnvironmentServer, не только в рамках подсистемы, но и за ними, - Transaction, - занимается управлением транзакциями. Для конструирования объекта транзакции в классе реализован статический метод CreateInstance(), на вход которого передается объект сервера окружения EnvironmentServer, пул соединений ConnectionPool и пул рабочих потоков ThreadPool. Метод CreateInstance() возвращает сконструированный и проинициализированный объект Transaction и используется в методе OpenTransaction() сервера EnvironmentServer, не требующем параметров и таким образом инкапсулирующем подробности внутренней реализации. На низком уровне методом Push() объекта ThreadPool в один из рабочих потоков помещается задание WorkItem. Оно инициализирует и связывает с текущим потоком включающую транзакцию CommittableTransaction, открывает соединение SqlConnection и получает соединение xNika из ConnectionPool, подписывает транзакцию на событие ее потока OnIdle с целью осуществления отката в случае превышения транзакцией таймаута в 1 час, инициализирует очередь транзакции, состоящую из заданий WorkItem, и классификатор используемых транзакцией файлов TransactionFile. Затем асинхронно ожидается завершение этого задания. Для выполнения заданий в транзакции созданы методы Invoke() и InvokeAsync(), принимающие на вход указатель на метод и его аргументы. Оба метода если транзакция не находится в состоянии завершения помещают задачу на выполнение: создают задание WorkItem, если в настоящий момент в очереди потока не находится ничего от имени этой транзакции, то она подписывается на события активации WorkActivating, исключения WorkException и завершения WorkComplete выполнения задания, а затем помещает это задание в очередь потока; иначе задание помещается в очередь транзакции. Если транзакция находится в состоянии завершения, поступающие от ее имени новые задания игнорируются. Разница методов состоит в том, что InvokeAsync() не ожидает завершения задания и возвращает интерфейс IAsyncResult объекта CWIAsync, отслеживающего завершение, в то время как метод Invoke() ожидает завершения задания и возвращает результат выполнения, упакованный в тип Object, кроме того, если во время выполнения произошла ошибка, метод генерирует соответствующее исключение. Текущее обрабатываемое в транзакции задание доступно через свойство транзакции CurrentWorkItem, так же как и ее соединение xNika, которое может использоваться заданием в произвольном порядке, доступно через свойство Connection. Транзакция так же имеет при себе контекст, представляющий собой словарь объектов, доступных по строковым идентификаторам. Для работы с контекстом предназначены методы AddContextObject(), добавляющий объект в контекст, и GetContextObject(), возвращающий объект по значению. Для работы с файлами в рамках транзакции предусмотрены методы CreateFile() и GetFile(), возвращающие объект TransactionFile. Метод CreateFile() принимает на вход полное имя файла, по которому создает объект TransactionFile и сохраняет его в списке используемых транзакцией файлов. Метод GetFile() возвращает из списка существующий TransactionFile по его идентификатору или null, если такового нет (описание класса - см. ниже). Для работы с менеджером блокировок BlockManager предусмотрены методы EnterLock(), TryEnterLock() и IsInLock(). Метод EnterLock() - основной метод для внесения блокировок, он принимает на вход идентификатор блокировки и создает на его основе объект CLockWorkItem, который затем добавляется в очередь потока или транзакции по тому же принципу, что и при вызове InvokeAsync(). Однако для внесения блокировки из задания данный метод не используется, а вместо него должен вызываться метод TryEnterLock(), который тоже принимает на вход идентификатор блокировки, сразу пытается ее выполнить и в случае успеха возвращает истину. При нарушении правила использования блокирующих методов генерируются соответствующие исключения. Метод IsInLock() проверяет, была ли внесена соответствующая передаваемому ему идентификатору блокировка.

Важную роль в работе всей подсистемы играет обработка классом транзакции событий WorkActivating, WorkComplete и WorkException, генерируемых объектом задания. Обработчик WorkActivating отмечает в контексте потока и в статическом поле класса Transaction текущую транзакцию, которая доступна через статическое свойство Current. Обработчик WorkComplete если завершившееся задание было типа CLockWorkItem и не закончилось успешно, добавляет в очередь потока заново его копию на выполнение; иначе в очередь рабочего потока добавляется очередное задание из очереди транзакции. Обработчик WorkException проверяет, когда произошла ошибка: если во время выполнения одного из заданий, либо на стадии фиксации, то вызывается метод Rollback() транзакции, выполняющий откат, а затем генерируется исключение; ошибка на стадии отката не обрабатывается и исключение генерируется сразу.

Для фиксации транзакции Transaction используется метод Commit(), а для отката - Rollback(). Метод Commit() выполняется только если не возникло никаких ошибок и не выполняется уже он или откат. Метод отписывает транзакцию от событий простоя ее потока, создает задание, которое обрабатывает событие OnCommit объекта транзакции, фиксирует соединение xNika и включающую транзакцию CommittableTransaction, применяет все изменения, внесенные транзакцией в файлы TransactionFile и помещает в очередь потока освобождение ресурсов, это задание помещается в очередь транзакции или рабочего потока, а затем метод Commit() ожидает его завершения. Освобождение ресурсов осуществляет функция FreeResources(): соединение xNika помечается в ConnectionPool как свободное, освобождает ресурсы, занятые соединением SqlConnection и файлами TransactionFile, удаляет все объекты из контекста, чтобы они не занимали память, выполняет обработчики события транзакции Complete и сигнализирует о завершении транзакции через ее свойство CompleteHandle. Метод Rollback() выполняется если не выполняется уже он или фиксация; метод отписывает транзакцию от событий простоя ее потока, создает задание, которое выполняет обработчики события транзакции OnRollback, выполняет откат включающей транзакции CommittableTransaction и ставит в очередь потока освобождение ресурсов, затем метод помещает это задание в очередь транзакции или рабочего потока и ожидает его завершения. Транзакция Transaction так же предоставляет возможность получения своего интервала простоя через свойство MyIdleInterval и численного уникального идентификатора через свойство ID.

Наконец, рассмотрим, что из себя представляет TransactionFile. Класс описывает файл, созданный или полученный в результате транзакции. Возможны два варианта: файл не существует, тогда он создается, в него что-то записывается и сохраняется, а при завершении транзакции файл удаляется, или файл существует и модифицируется во время транзакции, а при ее фиксации все изменения в файле фиксируются так же. В конструктор TransactionFile передается строка, определяющая полное имя файла; в конструкторе файлу присваивается уникальный строковый идентификатор, который затем доступен через свойство id. Полное имя файла доступно через свойство FullPath. Свойство transaction возвращает объект транзакции, которая работает с данным файлом. Метод GetStream() по заданному типу доступа возвращает файловый поток. Метод Flush() применяет к файлу все изменения.

.3.4 Практический анализ производительности подсистемы

С целью исследования производительности полученной подсистемы была реализована тестирующая процедура, имитирующая типичную последовательность действий при использовании системы и использующая подсистему управления транзакциями, а затем осуществлена проверка ее работы на трех различных тестовых конфигурациях. Для сравнения аналогичная процедура была реализована для старой версии АСУД «ЕВФРАТ», не использующей подсистему и таким же образом опробована на трех конфигурациях. Конфигурации стендов приведены в таблице 2.1:

Таблица 2.1. Конфигурация использованных стендов

Процессор

Тактовая частота

Объем оперативной памяти

Тип оперативной памяти

Операционная система

1

Intel Core 2 Quad Q9450

2.66 ГГц

2 ГБ

DDR2

Windows XP Professional

2

Intel Core 2 Duo E8200

2.66 ГГц

2ГБ

DDR2

Windows XP Professional

3

Intel Pentium 4 HT

2.4 ГГц

2 ГБ

DDR2

Windows XP Professional


Было произведено измерение количества выполненных тестов за фиксированный интервал времени - одну минуту. На каждом из перечисленных стендов подсистема управления транзакциями инициализировалась с количеством используемых рабочих потоков от 1 до 8, измерения производились для каждого из этих значений. Кроме этого, на каждом из стендов измерено количество тестов процедуры для старой версии. В результате были получены следующие результаты (в диаграммах для каждого случая приведены три значения - минимальное, среднее и максимальное количество выполнившихся тестов с использованием подсистемы, а так же результаты старой версии - отмечены уровнем затеняющего экрана):

Рисунок 2.1 - Зависимость количества выполненных тестов от числа потоков на 3 стенде

Рисунок 2.2 - Зависимость количества выполненных тестов от числа потоков на 2 стенде

Рисунок 2.3 - Зависимость количества выполненных тестов от числа потоков на 1 стенде

2.3.5 Рекомендации по использованию

Использование подсистемы с целью повышения производительности сервера приложений приобретает смысл при использовании процессоров с количеством ядер, отличным от единицы, или многопроцессорных систем. В любом случае установка при инициализации среды выполнения количества рабочих потоков больше числа процессоров на компьютере может привести к снижению суммарной производительности. В общем случае оптимальным числом рабочих потоков является количество процессоров в вычислительной системе.

Другая рекомендация касается улучшения читаемости программного кода. Для выполнения заданий в транзакции созданы методы Invoke() и InvokeAsync(), в которые в качестве указателя на метод удобно передавать так называемое лямбда-выражение - сокращенное объявление анонимного метода, ставшее возможным в C# 3.0. Типичный пример использования методов выглядит следующим образом:t = server.OpenTransaction(); //Создание транзакции

//Асинхронное выполнение.InvokeAsync(new Func<int>(() =>

{

//Список операторов

}));

//Синхронное выполнение.Invoke(new Func<int>(() =>

{

//Список операторов

}));

.3.6 Выводы

В данной главе проведен обзор и анализ существующих решений в области управления транзакциями в СУБД, рассмотрены пути увеличения производительности, использующиеся в системах управления распределенными транзакциями, изучены средства удаленного взаимодействия с объектами и выбраны средства организации управления транзакциями Microsoft .NET Framework. Предложенный алгоритм работы подсистемы управления транзакциями позволяет при выборе оптимальных параметров инициализации добиться существенного увеличения производительности серверной части АСУД «ЕВФРАТ». Реализован программный модуль - подсистема управления транзакциями на основе предложенного алгоритма, позволяющий создавать транзакции, в рамках транзакции выполнять операции с хранилищем и файлами, осуществлять фиксацию или откат транзакции пользователем и производить автоматический откат в случае возникновения ошибки, выполнять транзакции в заданном числе потоков - подсистема осуществляет распределение транзакций по потокам в зависимости от их загруженности для параллельной обработки, настраивать количество потоков для достижения максимальной производительности а так же интервал времени для периодических работ, планировать выполнение периодической работы во время простоя потоков, а так же не реже, чем через заданный интервал времени во время работы потоков. Полученная подсистема позволяет так же использовать технологию xNika для работы с данными, специфичными для автоматизированных системы управления документами, как с объектами - на высоком уровне. Проведено исследование показателей производительности системы с использованием разработанной подсистемы управления транзакциями и произведено сравнение с прежними показателями - в итоге были достигнуты лучшие показатели. Производительность алгоритмов оказалась заметной при тестировании на архитектуре с 2-ядерным процессором и значительно превышает прежние показатели на архитектуре с 4-ядерным процессором.

3. Экологическая часть и безопасность жизнедеятельности

.1 Исследование воздействия опасных и вредных факторов при эксплуатации ЭВМ и их воздействие на организм человека

Любой производственный процесс, в том числе работа с ЭВМ, связан с появлением опасных и вредных факторов. Опасным производственным фактором является такой фактор производственного процесса, воздействие которого на работающего приводит к травме или резкому ухудшению здоровья. Вредные производственные факторы - это неблагоприятные факторы трудового процесса или условий окружающей среды, которые могут оказать вредное воздействие на здоровье и работоспособность человека. Длительное воздействие на человека вредного производственного фактора приводит к заболеванию.

Негативные факторы трудового процесса приводят к снижению трудоспособности и ухудшению качества выпускаемой продукции. Длительное воздействие неблагоприятных условий труда может привести к нарушению здоровья работающего, развитию профессионального заболевания или инвалидности.

Охрана труда - это система обеспечения безопасности жизни и здоровья работников в процессе трудовой деятельности, включающая в себя правовые, социально-экономические, организационно-технические, санитарно-гигиенические, лечебно-профилактические, реабилитационные и иные мероприятия.

Задачей охраны труда является гарантирование безопасных и здоровых условий труда и поддержание трудоспособности рабочих. Безопасными условиями труда считаются такие условия, при которых воздействие на работающих вредных или опасных производственных факторов исключено либо уровни их воздействия не превышают установленные нормативы.

Для разработки программного обеспечения в данной дипломной работе были использованы следующие основные элементы вычислительной техники:

.        Персональный компьютер IBM PС:

·        частота процессора - 3000 МГц;

·        оперативная память - 2Гб;

·        жесткий диск - 80Гб;

·        DVD-ROM;

·        напряжение питания - 220 В.

.        Монитор SyncMaster 931BF:

·        потребляемая мощность - 38 Вт;

·        величина зерна - 0,294 мм;

·        максимальное разрешение - 1280 x 1024 при 75Гц;

·        напряжение питания - 220 В.

При использовании указанных элементов вычислительной техники могут возникнуть опасные и вредные факторы.

. Безопасным для человека считается напряжение менее 40В. Персональный компьютер питается от двухфазной сети переменного тока с частотой 50Гц и напряжением 220В. Это напряжение является опасным для человека, поскольку прикосновение к токоведущим частям может привести к поражению электрическим током, что обуславливает появление опасного фактора - поражение электрическим током.

Воздействие электрического тока на человека может носить следующий характер:

·        термический - нагрев тканей,

·        электролитический - влияние на состав крови,

·        биологический - раздражение нервных окончаний тканей, судорожное сокращение мышц,

·        механический - разрыв тканей, получение ушибов, вывихов как следствие потери координации движений после поражения электрическим током.

При поражении электрическим током человек может получить травмы следующего вида:

Общие травмы - электроудары. Различают электроудары четырех степеней сложности:

·        первая степень: судорожное болезненное сокращение мышц без потери сознания;

·        вторая степень: судорожное болезненное сокращение мышц, сопровождающееся потерей сознания, но с сохранением дыхания и сердцебиения;

·        третья степень: судорожное сокращение мышц, потеря сознания, нарушение работы сердца или дыхания (либо и то, и другое вместе);

·        четвертая степень: наступление клинической смерти, т.е. прекращение дыхания и кровообращения.

Местные травмы - ожоги, металлизация кожи, электрические знаки, уплотнение кожи, электроофтальмия, механические повреждения.

. При работе за экраном монитора человек попадает под воздействие излучения электромагнитных полей кадровой и строчной разверток на малых частотах, что вызывает появление вредного фактора - излучение электромагнитных полей низкой частоты. Данный вредный фактор влияет на человека следующим образом:

·        может вызвать обострение некоторых кожных заболеваний: угревой сыпи, себорроидной экземы, розового лишая, рака кожи и др.;

·        воздействие на метаболизм может вызвать изменение биохимической реакции крови на клеточном уровне, что ведет к стрессу;

·        может возникнуть нарушение в протекании беременности;

·        способствует увеличению возможности выкидыша у беременных в два раза;

·        способствует нарушению репродуктивной функции и возникновению злокачественных образований;

·        способствует нарушению терморегуляции организма;

·        способствует изменениям в нервной системе (потере порога чувствительности);

·        может привести к понижению/повышения артериального давления;

·        может привести к функциональным нарушениям сердечно-сосудистой и центральной нервной систем человека.

Результатом длительного нахождения в мощном ореоле низкочастотных электрических полей могут стать головные боли и нарушение визуального восприятия изображения.

. Во время работы на персональных ЭВМ при прикосновении к любому из элементов оборудования могут возникнуть разрядные токи статического электричества. Вследствие этого происходит электризация пыли и мелких частиц, которые притягивается к экрану. Собравшаяся на экране электризованная пыль ухудшает видимость, а при повышении подвижности воздуха в помещении более 0.2 м/с, попадает на лицо и в легкие человека, вызывая заболевания кожи и дыхательных путей. Статическое электричество при превышении нормированного значения 15 кВ/м становится вредным фактором.

Особенно электростатический эффект наблюдается у компьютеров, которые находятся в помещении с полами, покрытыми синтетическими коврами.

При повышении напряженности поля Е>15 кВ/м, статическое электричество может привести к выходу компьютера из строя, замыканию клавиатуры и потере информации на экране.

. К вредным факторам при работе за компьютером можно также отнести блики и мерцание экрана из-за низкой частоты вертикального обновления или из-за низкого качества развертки монитора. Экспериментальные данные показывают, что вышеуказанные факторы способствуют возникновению:

·        близорукости и переутомлению глаз;

·        мигрени и головной боли;

·        раздражительности, нервному напряжению и стрессу.

При работе за компьютером для вывода информации на бумажный носитель применяется принтер. Но, поскольку, принтер работает кратковременно, вредного воздействия на человека по шуму он не оказывает.

Из анализа опасных и вредных факторов, возникающих при работе на компьютере, можно сделать вывод, что оператор нуждается в средствах защиты от их воздействия.

.2 Способы защиты пользователей от опасных и вредных факторов

Для защиты от напряжения прикосновения используется зануление. Занулением называется преднамеренное соединение нетоковедущих частей с нулевым защитным проводником (рис. 3.1). Оно применяется в трехфазных четырехпроводных сетях с заземленной нейтралью в установках до 1000 вольт и является основным средством обеспечения электробезопасности.

Защита человека от поражения электротоком в сетях с заземлением осуществляется тем, что при замыкании одной из фаз на зануленный элемент машины в цепи этой фазы возникает ток короткого замыкания, который вызывает перегорание предохранителя (автомата) в оборудовании, в результате чего происходит отключение аварийного участка от сети.

Рисунок 3.1 - Зануление

Расчет возможной величины тока короткого замыкания производится на основе следующих данных:

.        Коэффициент, учитывающий тип защитного устройства: k=3;

.        Фазовое напряжение: U=220В;

3.            Паспортная величина сопротивления обмотки трансформатора:

= 0.312 Ом;

.        Удельное сопротивление медных проводников: =0.0175Ом*м;

.        Удельное сопротивление алюминиевых проводников: =0.0280Ом*м;

.        Расчет величины возможного тока короткого замыкания по заданным параметрам:

,(3.1)

где Iкз - ток короткого замыкания [А];ф - фазовое напряжение [B];общ - общее сопротивление цепи [Ом];т- сопротивление катушек трансформатора [Ом].

,(3.2)

где r1 и r2 - сопротивление первого и второго проводника соответственно [Ом];нзп - сопротивление нулевого защитного проводника [Ом];

7.      Расчет сопротивления проводника производится по формуле:

,(3.3)

где  - удельное сопротивление материала проводника [Ом*м];- длина проводника [м];- площадь поперечного сечения проводника [мм2].

Таким образом, получаем следующие значения:

= 0.0280*(500/2)=7 (Ом);

= 0,0175*(20/1)=0.35(Ом);

= 0,0175*(15/1)=0.2625(Ом);

 = 7+0.35+0.2625=7.6125 (Ом);

 = 220/(0.312/ 3 + 7.6125) = 28.51 (А).

По величине тока короткого замыкания  определим с каким  необходимо к цепь питания ПЭВМ включить автомат. При замыкании фазы на зануленный корпус электроустановка автоматически отключается, если ток однофазного короткого замыкания  удовлетворяет условию:

 ,(3.4)

где  - номинальный ток срабатывания защитного устройства, [А];- коэффициент, учитывающий тип защитного устройства.

Таким образом, номинальный ток срабатывания защитного устройства:

.(3.5)

= 28.51/3=9.5 (А).

Отсюда следует, что во избежание поражения электрическим током и выхода из строя ПЭВМ и периферийного оборудования, в случае возникновения короткого замыкания или других причин появления напряжения прикосновения в цепь питания ПЭВМ необходимо включить автомат с номинальным током = 10А.

Для снижения уровня воздействия электромагнитных полей желательно пользоваться следующими мерами:

·        Осуществлять экранирование экрана монитора, суть которого заключается в покрытии поверхности экрана слоем оксида олова, либо в стекло электролучевой трубки добавляется оксид свинца;

·        Удалять рабочее место от источника электромагнитного поля: оператор должен находиться на расстоянии вытянутой руки от экрана монитора;

·        Необходимо рациональное размещение оборудования. Располагать ЭВМ следует на расстоянии не менее 1.22 м от боковых и задних стенок других мониторов;

·        Запрещается работать при снятых внешних кожухах (корпусах) персональных компьютеров;

·        Необходимо ограничивать время работы за компьютером. Время непрерывной работы должно составлять не более 4 часов в сутки. За неделю суммарное время работы не должно превышать 20 часов.

Защита от статического электричества достигается:

.        Установкой контурного заземления;

.        Установкой нейтрализаторов статического электричества;

.        Использованием экранов для снятия статики;

.        Наличием антистатичных полов;

.        Проведением влажной уборки;

.        Проветриванием помещений с подвижностью воздуха 0,1 - 0,2 м/с без присутствия пользователей.

Мерцание экрана зависит исключительно от характеристик монитора, поэтому уменьшить воздействие данного вредного фактора можно лишь, уменьшив время, проведенное за экраном монитора.

Блики на экране монитора могут возникнуть из-за неправильного освещения в помещении.

.3 Выводы

транзакция пользователь управление

При использовании человеком вычислительной техники могут возникнуть опасные и вредные факторы, такие как: поражение электрическим током, излучение электромагнитных полей низкой частоты, статическое электричество. Необходимо не только знать о влиянии на человека данных факторов, но и уметь обезопасить себя от их воздействия. Безопасность при работе с ЭВМ может быть обеспечена за счет:

·        правильного выбора визуальных параметров дисплея,

·        рационального размещения компьютеров в помещениях,

·        правильного подбора освещения и микроклимата в помещении.

Используемые методы и способы по защите от воздействия опасных и вредных факторов обеспечивают безопасность разработчика и пользователей.

Заключение

В результате выполнения дипломного проекта получены следующие результаты:

.        Проведен обзор и анализ существующих решений в области управления транзакциями в СУБД, рассмотрены пути увеличения производительности, использующиеся в системах управления распределенными транзакциями, изучены средства удаленного взаимодействия с объектами и выбраны средства организации управления транзакциями Microsoft .NET Framework;

.        Предложен алгоритм работы подсистемы управления транзакциями, позволяющий при выборе оптимальных параметров инициализации добиться существенного увеличения производительности серверной части системы «ЕВФРАТ»;

.        Реализован программный модуль - подсистема управления транзакциями на основе предложенного алгоритма, позволяющий:.    Создавать транзакции;.        В рамках транзакции выполнять операции с хранилищем и файлами;.    Осуществлять фиксацию или откат транзакции пользователем и производить автоматический откат в случае возникновения ошибки;.       Выполнять транзакции в заданном числе потоков - подсистема осуществляет распределение транзакций по потокам в зависимости от их загруженности для параллельной обработки;.         Настраивать количество потоков для достижения максимальной производительности, а так же интервал времени для периодических работ;.          Планировать выполнение периодической работы во время простоя потоков, а так же не реже, чем через заданный интервал времени во время работы потоков;.         Использовать технологию xNika для работы с данными, специфичными для системы «ЕВФРАТ», как с объектами - на высоком уровне - упрощенным способом.

.        Проведено исследование показателей производительности системы с использованием разработанной подсистемы управления транзакциями и произведено сравнение с прежними показателями - в итоге были достигнуты лучшие показатели;

.        Экологическая часть содержит исследование опасных и вредных факторов при работе с ЭВМ и методы защиты пользователей от их воздействия.

Подсистема управления транзакциями, полученная в результате выполнения дипломного проекта, может быть использована в качестве инструментария для создания различных решений в сфере электронного документооборота и близких к ним систем.

Список литературы


1       The Notions of Consistency and Predicate Locks in a Database System - K. Eswaran, J. Gray, R. Lorie, I. Traiger - Communications of the ACM. - 1976. - №19. - С. 624-633. - ISSN:0001-0782

         Основы систем баз данных - Дж. Ульман - Финансы и статистика, Москва. - 1983. - 334 с

         On Optimistic Methods for Concurrency Control - H.T. Kung, J.T. Robinson - ACM Transactions on Database Systems. - 1981. - №6(2). - С.213-226

         Optimistic Concurrency Control Algorithm with Dynamic Serialization Adjustment for Firm Deadline Real-Time Database Systems - P. Krzyzagrski, T. Morzy - Second International Workshop on Advances in Databases and Information Systems (ADBIS"95) . - 1995. - №1. - С.21-28

         Concurrency control performance modeling: alternatives and implications - R. Agrawal, M.J. Carey, M. Livny - ACM Transactions on Database Systems. - 1987. - №12(4). - С.609-654

         The performance of Concurrency Control Algorithms for Database management Systems - M.J. Carey, M.R. Stonebraker - 10th VLDB Conference, Singapore. - 1984

         Real-Time Databases - K. Ramamritham - International Journal of Distributed and Parallel Databases. - 1992. - №1(1)

         Scheduling Real-Time Transactions: A performance Evaluation - R. Abbott, H. Garcia-Molina - 14th VLDB Conference. - 1988

         Scheduling Real-Time Transactions with Disk Resident Data - R. Abbott, H. Garcia-Molina - 15th VLDB Conference. - 1989

         Dynamic Real-Time Optimistic Concurrency Control - J.R. Haritsa, M.J. Carey, M. Livny - IEEE Real-Time Systems Simposium, Orlando, Florida. - 1990

         Введение в системы баз данных - К.Дж. Дейт - Изд. Дом «Вильямс». - 2008. - 1328 с. - ISBN 978-5-8459-0788-2, 0-321-19784-4

         C# 2005 и платформа .NET 3.0 для профессионалов - К. Нейгл, Б. Ивьен, Д. Глинн, М. Скиннер и др. - Диалектика. - 2008. - 1376 с. - ISBN 978-5-8459-1317-3

         От баз данных к базам знаний (объекты, формы, содержание) - Арлазаров В.Л., Емельянов Н.Е. - 2010

         Электронный документооборот по технологии клиент-сервер - Даниленко А.Ю., Подрабинович А.А., Сургучев В.А., Хлюстов К.В. - 2004

         Особенности систем, создаваемых на основе НИКА - Технологии - Емельянов Н.Е., Ерохин В.И. - 2009

         Богданов А.С., Емельянов Н.Е., Ерохин В.И., Скорняков В.А., Романов Б.Л. НИКА-технология построения информационных систем - Организационное управление и искусственный интеллект - Сб. трудов ИСА РАН. Под ред. члена-корр. РАН Арлазарова В.Л. и д.т.н. проф. Емельянова Н.Е. - М.: Едиториал УРСС, 2003.

Приложения

Текст программы

using System;System.Collections.Generic;System.Linq;System.Text;System.Threading;Cognitive.Threading

{

/// <summary>

/// делегат для обработки событий пула потоков

/// </summary>

/// <param name="source"></param>delegate void ThreadPoolEvent(ThreadPool source);

/// <summary>

/// пул потоков для обработки заданий

/// </summary>class ThreadPool

{List<WorkingThread> m_threads = new List<WorkingThread>();

/// <summary>

/// конструктор объекта

/// </summary>

/// <param name="count">Количество рабочих потоков

/// <remarks>установка количества рабочих потоков больше числа

/// процессоров на компьютере

/// может привести к снижению суммарной производительности</remarks>

/// </param>

/// <param name="idleInterval">максимальный интервал между обработкой событий простоя

/// <see cref="ThreadPool.OnIdle"/>

/// </param>

/// <remarks>

/// Выполняет запуск указанного количества рабочих потоков.

/// </remarks>ThreadPool(int count, TimeSpan idleInterval)

{(count <= 0)new ArgumentException("Количество потоков должно быть неотрицательным числом или неравно нулю");(idleInterval <= TimeSpan.Zero)new ArgumentException("Максимальный интервал между обработкой события простоя не должен быть отрицательным числом.");(idleMinInterval > idleInterval)= idleInterval;(int i = 0; i < count; i++)

{thread = new WorkingThread(.FromTicks(idleMinInterval.Ticks * 2/ 3), idleInterval);.OnIdle += new EventHandler(thread_OnIdle);_threads.Add(thread);

}

}volatile bool _InIdle = false;DateTime lastIdle = DateTime.Now;

/// <summary>

/// минимальный интервал вызова обработчика события Idle

/// при отсуствии задач для обработки

/// </summary>TimeSpan idleMinInterval = TimeSpan.FromMilliseconds(100);void thread_OnIdle(object sender, EventArgs e)

{(OnIdle == null);(this)

{(_InIdle || (lastIdle + idleMinInterval) > DateTime.Now);

_InIdle = true;

}(this);= DateTime.Now;

_InIdle = false;

}

//private bool m_stopping = false;bool m_stopping = false;

/// <summary>

/// Пул находится в состоянии останова

/// </summary>bool Stopping

{

{m_stopping;

}

}

/// <summary>

/// останавливает все запущенные рабочие потоки

/// </summary>void Stop()

{_stopping = true;

//Блокируем потоки, меняем статус на останов(WorkingThread thread in m_threads)

{.OnIdle -= thread_OnIdle;.StartStop();

}

//Ждем освобождения их очередей(WorkingThread thread in m_threads).queue.Empty.WaitOne();

//Ждем пока потоки затерминируются(WorkingThread thread in m_threads).Stop();_threads.Clear();

}

/// <summary>

/// добавляет задачу для выполнения

/// </summary>

/// <param name="wi">Задача для выполнения</param>

/// <remarks>

/// добавляет задачу для выполнения в очередь одного из рабочих потоков.

/// Выбирается наименее загруженный поток -

/// который простаивает или тот, у которого наименее короткая очередь

/// </remarks>void Push(WorkItem wi)

{(m_stopping)new InvalidOperationException("Пул находится в состоянии останова и не может принимать задачи");

// throw new InvalidOperationException("Пул находится в состоянии останова и не может принимать задачи");(m_threads.Count == 0)new InvalidOperationException("В пуле нет ни одного потока");

//рассматриваю список тредов, если тредов несколькоthread = m_threads[0];(m_threads.Count > 1)

{

//Ищу наименее загруженные(int i = 1; i < m_threads.Count; i++)

{(((m_threads[i].Сount == thread.Сount && m_threads[i].State > thread.State)

|| m_threads[i].Сount < thread.Сount) && m_threads[i].State != WorkingThreadStates.Stopping)

{= m_threads[i];

}

}

}.queue.Push(wi);

}

/// <summary>

/// событие простоя

/// </summary>

/// <remarks>

/// события простоя возникает когда любой из рабочих потоков входит в состояние простоя или

/// с момента обработки последнего события простоя прошло времени больше, чем было указано

/// при конструировании объекта.

///

/// Событие простоя может использоваться для запуска фоновых задач

/// </remarks>event ThreadPoolEvent OnIdle;

}

}System;System.Collections.Generic;System.Linq;System.Text;System.Threading;System.Runtime.InteropServices;Cognitive.Threading

{

/// <summary>

/// состояния рабочих потоков

/// </summary>enum WorkingThreadStates

{

/// <summary>

/// поток находится в процессе запуска

/// </summary>= 1,

/// <summary>

/// поток находится в состоянии обработки задания из очереди

/// </summary>= 2,

/// <summary>

/// поток находится в состоянии ожидания очередного задания

/// </summary>= 3,

/// <summary>

/// поток находится в состоянии завершения

/// </summary>= 0

}

/// <summary>

/// рабочий поток

/// </summary>

/// <remarks>

/// Управляет очередью задач в рамках потока.

/// Все обращения к методам экземпляра класса являются потокозащищенными

/// </remarks>class WorkingThread

{WorkItemsQueue m_witems = new WorkItemsQueue();Thread m_thread = null;ThreadContext m_ctx = new ThreadContext();ManualResetEvent m_stoppingThread = new ManualResetEvent(false);volatile WorkingThreadStates m_state = WorkingThreadStates.Idle;DateTime lastIdle = DateTime.Now;TimeSpan idleMaxInterval = TimeSpan.Zero; //TimeSpan.FromSeconds(5);TimeSpan idleMinInterval = TimeSpan.Zero; //TimeSpan.FromSeconds(5);Func<TimeSpan, TimeSpan, TimeSpan> max = (x, y) => { if (x > y) return x; return y; };void Work()

{

//указываем текущий поток

__current = this;[] waitfor = new WaitHandle[] { m_witems.Received, m_stoppingThread };(!m_stoppingThread.WaitOne(0, false))

{

//Очередь пуста или таймаут ожидания обработки Idle

//и не в состоянии завершения(((!m_witems.Received.WaitOne(0, false))

|| (lastIdle + idleMaxInterval) < DateTime.Now)

&& (m_state != WorkingThreadStates.Stopping))

{_state = WorkingThreadStates.Idle;(OnIdle != null)(this, EventArgs.Empty);= DateTime.Now;

}waitRes = WaitHandle.WaitAny(waitfor, idleMinInterval, false);(waitRes != 0) //останов или таймаут;

//waitRes = 0, что-то помещено в очередь(m_state != WorkingThreadStates.Stopping)_state = WorkingThreadStates.Working;(!m_stoppingThread.WaitOne(0, false) &&

(m_witems.Length > 0) &&

(lastIdle + idleMaxInterval) > DateTime.Now)

{wi = null;(m_witems.TryGet(out wi))

{.Clear();.Do();

}

}

}

}

/// <summary>

/// Общее количество задач в потоке

/// </summary>int Сount

{

{m_witems.Length;

}

}

/// <summary>

/// очередь задач потока (не содержит ожидающих блокировку задач, которые могут быть в потоке)

/// </summary>WorkItemsQueue queue { get { return m_witems; } }

/// <summary>

/// конструктор потока

/// </summary>

/// <param name="idleMax">Максимальный интервал между обработками события Idle, д.б. больше 5 сек</param>

/// <remarks>

/// выполняет запуск рабочего потока для выполнения задач

/// </remarks>WorkingThread(TimeSpan idleMin, TimeSpan idleMax)

{(idleMin > idleMax)new ArgumentException();_state = WorkingThreadStates.Starting;_thread = new Thread(Work);= idleMax;= idleMin;_thread.Start();_thread.IsBackground = false;

}event EventHandler OnIdle;

/// <summary>

/// останавливает поток.

/// </summary>

/// <remarks>

/// Блокирует добавление новых заданий в поток и завершает выполнения потока.

/// Выполняется с ожиданием завершения потока.

/// Эта функция не может вызвана из самого рабочего потока

/// </remarks>void StartStop()

{_state = WorkingThreadStates.Stopping;_witems.Block();

}

/// <summary>

/// инициирует процедуру остановки потока без ожидания завершения и без блокировки

/// </summary>void Stop()

{_stoppingThread.Set();_thread.Join();

}

/// <summary>

/// состояния потока в настоящее время

/// </summary>WorkingThreadStates State

{{m_state;

}

}

/// <summary>

/// контекст выполнения текущей задачи

/// </summary>

/// <remarks>контекст выполнения задачи инициализируется классами,

/// который помещают задачу в поток, в обработчике события WorkItem.WorkStarting.

/// Контекст автоматически очищается после выполнения каждой задачи

/// </remarks>ThreadContext Context

{{m_ctx;

}

}

/// <summary>

/// Идентификатор соответствующего потока

/// </summary>int ManagedThreadId

{

{m_thread.ManagedThreadId;

}

}

[ThreadStatic]static WorkingThread __current = null;

/// <summary>

/// возвращает текущий объект рабочего потока

/// </summary>

/// <remarks>Если вызов производится в рамках рабочего потока, то

/// возвращает соответствующий объект рабочего потока,

/// иначе позвращает null

/// </remarks>static WorkingThread Current

{

{__current;

}

}

}

/// <summary>

/// контекст выполнения задачи в рабочем потоке

/// </summary>class ThreadContext : System.Collections.Generic.Dictionary<string, Object>

{

}

}System;System.Collections.Generic;System.Linq;System.Text;System.Threading;Cognitive.Threading

{

/// <summary>

/// очередь задач на исполнение

/// </summary>

/// <remarks>

/// Все обращения к методам экземпляра класса являются потокозащищенными

/// </remarks>class WorkItemsQueue

{Queue<WorkItem> m_queue = new Queue<WorkItem>();bool m_blocked = false;ManualResetEvent m_received = new ManualResetEvent(false);ManualResetEvent m_empty = new ManualResetEvent(true);

/// <summary>

/// В очереди появились задачи

/// </summary>ManualResetEvent Received

{

{m_received;

}

}

/// <summary>

/// Очередь освободилась

/// </summary>ManualResetEvent Empty

{

{m_empty;

}

}

/// <summary>

/// добавляет задачу в очередь

/// </summary>

/// <param name="wi">Задача для выполнения</param>void Push(WorkItem wi)

{(m_queue)(!m_blocked)

{_queue.Enqueue(wi);_received.Set();_empty.Reset();

}new ArgumentException("Рабочий поток в состоянии завершения и не может принимать workitems");

}

/// <summary>

/// попытка получить очередную задачу из очереди

/// </summary>

/// <param name="wi">Возвращаемый параметр - задача для исполнения</param>

/// <returns>Если задача успешно получена, то возвращается true.

/// Если очередь пуста - то возвращается false</returns>bool TryGet(out WorkItem wi)

{= null;(m_queue)

{(m_queue.Count == 0)false;= m_queue.Dequeue();(m_queue.Count == 0)

{_received.Reset();_empty.Set();

}

}wi != null;

}

/// <summary>

/// ожидание появления в очереди задачи для выполнения

/// </summary>

/// <param name="ts">Время в течение которого выполнять ожидание</param>

/// <returns>true если ожидание выполнено успешно и задача в очереди появилась,

/// false если указанное время прошло, а задачи так и не появилось</returns>

/// <remarks> Пока выполняется ожидание вызывающий поток не работает.</remarks>bool Wait(TimeSpan ts)

{m_received.WaitOne(ts, false);

}

/// <summary>

/// блокировка очереди

/// </summary>

/// <remarks>запрещает добавление новых элементов в очередь</remarks>void Block()

{_blocked = true;

}

/// <summary>

/// является ли очередь заблокированной

/// </summary>bool Blocked

{

{m_blocked;

}

}

/// <summary>

/// длина очереди

/// </summary>int Length

{

{(m_queue)

{m_queue.Count;

}

}

}

}

}System;System.Collections.Generic;System.Linq;System.Text;System.Threading;System.Linq.Expressions;System.Runtime.InteropServices;Cognitive.Threading

{

/// <summary>

/// делегат события задачи, выполняющейся в пуле потоков

/// </summary>

/// <param name="source"></param>delegate void WorkItemEventDelegate(WorkItem source);

/// <summary>

/// делегат события об ошибке, возникшей при выполнении задачи в пуле потоко

/// </summary>

/// <param name="source"></param>

/// <param name="e"></param>delegate void WorkItemExceptionDelegate(WorkItem source, Exception e);

/// <summary>

/// задача для выполнения в пуле потоков

/// </summary>class WorkItem

{m_completeHandle = new ManualResetEvent(false);m_ItemsMethod;[] m_args;m_result = null;m_stack;

/// <summary>

/// конструктор задачи для выполнения

/// </summary>

/// <param name="method"></param>

/// <param name="parameters"></param>WorkItem(Delegate method, params Object[] parameters)

{_ItemsMethod = method;_args = parameters;_stack = (new System.Diagnostics.StackTrace(1)).ToString();

}

/// <summary>

/// событие начала работы над задачей

/// </summary>event WorkItemEventDelegate WorkStarting;

/// <summary>

/// событие активизации задачи для инициализации контекста

/// </summary>event WorkItemEventDelegate WorkActivating;

/// <summary>

/// событие окончания работы над задачей

/// </summary>event WorkItemEventDelegate WorkComplete;

/// <summary>

/// событие об исключении в процессе выполнения задачи

/// </summary>event WorkItemExceptionDelegate WorkException;void Do()

{

{();();();

}(System.Exception ex)

{(ex);

}

}

}void ProcessException(Exception e)

{(WorkException != null)(this, e);e;

}void Complete()

{(WorkComplete != null)

{(this);

}_completeHandle.Set();

}void Invoke()

{(m_ItemsMethod != null)_result = m_ItemsMethod.DynamicInvoke(m_args);

}void Start()

{(WorkStarting != null)(this);

}void Activate()

{(WorkActivating != null)(this);

}

/// <summary>

/// результат выполнения задачи

/// </summary>Object Result

{

{m_result;

}

}

/// <summary>

/// Объект для асинхронного ожидания завершения выполнения задания. Может означать так же

/// принудительное завершение при останове пула потоков

/// </summary>WaitHandle AsyncWaitHandle

{

{m_completeHandle;

}

}delegate void anywork();delegate object anyworkandret();

}

}System;System.Collections.Generic;System.Linq;System.Text;Cognitive.Docflow.Transactions;System.Diagnostics;Cognitive.Docflow.Environment

{class BlockManager

{

/// <summary>

/// словарь активных блокировок

/// </summary><String, Transaction> _activeLocks = new Dictionary<string, Transaction>();

/// <summary>

/// попытка войти в блокировку в текущей транзакции

/// </summary>

/// <param name="blockId">идентификатор блокировки</param>

/// <returns>результат - удалось или нет</returns>bool TryEnterBlock(String blockId)

{(_activeLocks)

{trans;(!_activeLocks.TryGetValue(blockId, out trans))

{.Current.Complete += new TransactionEventHandler(Current_Complete);

_activeLocks.Add(blockId, Transaction.Current);.WriteLine("EnterLock: " + blockId + " trans: " + Transaction.Current.ID);true;

}(trans.Equals(Transaction.Current))

{.WriteLine("EnterLock: " + blockId + " trans: " + Transaction.Current.ID);true;

}false;

}

}bool IsInLock(Transaction test, String lockId)

{(_activeLocks)

{trans;(_activeLocks.TryGetValue(lockId, out trans))test.Equals(trans);false;

}

}Current_Complete(Transaction sender, EventArgs e)

{

///удаление всех активных блокировок для указанной транзакции(_activeLocks)

{<string> keys = new List<string>();(KeyValuePair<String, Transaction> pair in

_activeLocks.Where((KeyValuePair<String, Transaction> p ){p.Value.Equals(sender);

}))

{.WriteLine("LeaveLock: " + pair.Key + " trans: " + sender.ID);.Add(pair.Key);

}(string k in keys)

{

_activeLocks.Remove(k);

}

}

}

}

}System;System.Collections.Generic;System.Linq;System.Text;Cognitive.Docflow.Transactions

{

/// <summary>

/// класс, управляющий набором соединений со средой хранения

/// </summary>

/// <remarks>

/// Управляет соединениями.

/// Каждое соединение используется в том потоке, в котором было создано.

/// Для каждого потока существует свой набор соединений.

/// Соединение бывает свободным и занятым.

/// </remarks>class ConnectionPool

{static CTNKDESMLib.ICTDEConnector s_conn = new XNika.SQL.XNSQLConnector();

/// <summary>

/// Состояния соединения

/// </summary>enum ConnectionStateEnum

{

/// <summary>

/// Соединение занято

/// </summary>,

/// <summary>

/// Соединение свободно

/// </summary>

}String m_sqlConnString;Dictionary<int, Dictionary<CTNKDESMLib.ICTDEDatabase, ConnectionStateEnum>> m_connections;String m_login, m_password;

/// <summary>

/// строка подключени к MS SQL

/// </summary>String ConnectionString

{{ return m_sqlConnString; }

}

/// <summary>

/// конструктор

/// </summary>

/// <param name="sqlConnectionString"></param>

/// <param name="login"></param>

/// <param name="password"></param>ConnectionPool(String sqlConnectionString, String login, String password)

{_login = login;_password = password;_sqlConnString = sqlConnectionString;_connections = new Dictionary<int, Dictionary<CTNKDESMLib.ICTDEDatabase, ConnectionStateEnum>>();

}

/// <summary>

/// возвращает свободное соединение для текущего потока.

/// В случае отсутствия свободного соединения создается новое

/// Возвращаемое соединение отмечается как занятое и остается таким до вызова

/// функции FreeConnection

/// </summary>

/// <returns></returns>CTNKDESMLib.ICTDEDatabase GetConnection()

{currThread = System.Threading.Thread.CurrentThread.ManagedThreadId;.ICTDEDatabase db;<CTNKDESMLib.ICTDEDatabase, ConnectionStateEnum> threadConnections = null;(m_connections)

{(m_connections.ContainsKey(currThread))

{= m_connections[currThread];

//Ищем свободное(threadConnections.ContainsValue(ConnectionStateEnum.FREE))(KeyValuePair<CTNKDESMLib.ICTDEDatabase, ConnectionStateEnum> pair in threadConnections)(pair.Value == ConnectionStateEnum.FREE)pair.Key;

}

}

//Нужно новое соединение создаем= s_conn.Open(m_sqlConnString, m_login, m_password);

///формат поиска

((CTNKDESMLib.ICTDEDatabase2)db).queryFormat = CTNKDESMLib.tagCTDEQueryFormat.CTDE_QF_XPATH_STRING;(threadConnections == null)

{

//если нет словаря соединений для текущего потока - добавляем= new Dictionary<CTNKDESMLib.ICTDEDatabase, ConnectionStateEnum>();(m_connections)

{_connections.Add(currThread, threadConnections);

}

}

//добавляем новое соединение в словарь.Add(db, ConnectionStateEnum.BUSY);db;

}

/// <summary>

/// помечает указанное соединение как свободное

/// </summary>

/// <param name="connection"></param>void FreeConnection(CTNKDESMLib.ICTDEDatabase connection)

{currThread = System.Threading.Thread.CurrentThread.ManagedThreadId;(m_connections)

{(m_connections.ContainsKey(currThread) &&_connections[currThread].ContainsKey(connection))_connections[currThread][connection] = ConnectionStateEnum.FREE;

}

}

}

}System;System.Collections.Generic;System.Linq;System.Text;Cognitive.Docflow;System.Collections;Cognitive.Threading;System.Data.SqlClient;System.Data.Common;System.Diagnostics;Cognitive.Docflow.Transactions

{

/// <summary>

/// делегат конструирования корневого объекта бизнес-логики

/// </summary>

/// <param name="datasource"></param>

/// <returns></returns>delegate Environment.IDFContainerObject(CTNKDESMLib.ICTDEDatabase datasource);

/// <summary>

/// делегат события транзакции

/// </summary>

/// <param name="sender"></param>

/// <param name="e">параметры события</param>delegate void TransactionEventHandler(Transaction sender, EventArgs e);

/// <summary>

/// объект транзакции

/// </summary>class Transaction: Environment.IDFObjectsFactory

{static volatile int _index = 0;DateTime _MyStartTime;DateTime _MyIdleStart;

/// <summary>

/// Время простоя транзакции

/// </summary>TimeSpan MyIdleInterval

{

{(_running == null)DateTime.Now - _MyIdleStart;TimeSpan.Zero;

}

}int _MyIndex;ConnectionPool _cpool;System.Transactions.CommittableTransaction _cTrans;_sqlConn;CTNKDESMLib.ICTDEDatabase _connection;Environment.EnvironmentServer _server;TransactionContext _ctx;Dictionary<String, TransactionFile> _transactionFiles;

/// <summary>

/// Очередь задач транзакции - задачи в ожидании

/// </summary>Queue<Cognitive.Threading.WorkItem> _transactionQueue;

/// <summary>

/// обрабатываемая в настоящий момент задача

/// </summary>WorkItem _running;

/// <summary>

/// Транзакция находится в состоянии завершения и не может принимать новых задач

/// </summary>bool _commitFlag = false;bool _rollbackFlag = false;object _locker = new object();System.Threading.ManualResetEvent _completeHandle = new System.Threading.ManualResetEvent(false);

/// <summary>

/// Устанавливается, когда транзакция выполнилась и освободила свои ресурсы

/// </summary>System.Threading.WaitHandle CompleteHandle

{

{_completeHandle;

}

}

/// <summary>

/// рабочий поток в котором работает транзакция

/// </summary>WorkingThread _myThread;

/// <summary>

/// конструктор объекта Транзакция

/// </summary>

/// <param name="ctor">делегат конструирования корневого объекта бизнес-логики</param>

/// <param name="cpool">пул соединений</param>

/// <param name="thrd">рабочий поток, соответствующий транзакции</param>Transaction(Environment.EnvironmentServer server,cpool, Cognitive.Threading.WorkingThread thrd)

{

_MyIndex = ++_index;

_MyStartTime = DateTime.Now;

//тайм-аут у транзакции 1 час

_cTrans = new System.Transactions.CommittableTransaction(TimeSpan.FromHours(1));.Transactions.Transaction.Current = _cTrans;

_sqlConn = new SqlConnection(cpool.ConnectionString);

_sqlConn.Open();.Samples.DbConnectionScope.DbConnectionScope _conScope = new Microsoft.Samples.DbConnectionScope.DbConnectionScope();

_conScope.AddConnection(cpool.ConnectionString, _sqlConn);(_conScope)

{

_server = server;

_ctx = new TransactionContext();

_connection = cpool.GetConnection();

_cpool = cpool;

_myThread = thrd;

_myThread.OnIdle += new EventHandler(_myThread_OnIdle);

_transactionFiles = new Dictionary<string, TransactionFile>();

_transactionQueue = new Queue<Cognitive.Threading.WorkItem>();

_conScope.AddConnection(cpool.ConnectionString, new SqlConnection());

}

_MyIdleStart = DateTime.Now;

}_myThread_OnIdle(object sender, EventArgs e)

{(_commitFlag || _rollbackFlag);

//Таймаут транзакции = 1 час((DateTime.Now - _MyStartTime) < TimeSpan.FromHours(1));

_myThread.OnIdle -= _myThread_OnIdle;();

}int ID

{

{_MyIndex;

}

}

/// <summary>

/// освобождение ресурсов транзакции

/// </summary>

/// <remarks>

/// вызывается из методов Commit или Rollback

/// </remarks>void FreeResources()

{

_cpool.FreeConnection(_connection);

_sqlConn.Dispose();(_locker)

{(KeyValuePair<String, TransactionFile> pair in _transactionFiles).Value.Dispose();

_transactionFiles.Clear();

_ctx.Clear();

}(Complete != null)(this, null);

_completeHandle.Set();

}

/// <summary>

/// Создание экземпляра новой транзакции

/// </summary>

/// <param name="server"></param>

/// <param name="cpool">пул соединений</param>

/// <param name="thpool">пул рабочих потоков</param>

/// <returns>сконструированный и проинициализированный объект транзакции</returns>static Transaction CreateInstance(Environment.EnvironmentServer server,cpool, Cognitive.Threading.ThreadPool thpool)

{(thpool.Stopping)new InvalidOperationException("Нельзя создавать новые транзакции когда пул потоков в состоянии завершения");.Threading.WorkingThread wthr = Cognitive.Threading.WorkingThread.Current;(wthr == null)

{.Threading.WorkItem.anyworkandret CreateTrans = () =>

{Transaction.CreateInstance(server, cpool, thpool);

};.Threading.WorkItem wi =Cognitive.Threading.WorkItem(CreateTrans);.Push(wi);.AsyncWaitHandle.WaitOne();wi.Result as Transaction;

}

{new Transaction(server, cpool, wthr);

}new Exception();

}

/// <summary>

/// вызван метод Commit

/// </summary>event TransactionEventHandler OnCommit;

/// <summary>

/// вызыван метод Rollback

/// </summary>event TransactionEventHandler OnRollback;

/// <summary>

/// транзакция завершена (независимо от результата)

/// </summary>event TransactionEventHandler Complete;

/// <summary>

/// откат транзакции

/// </summary>void Rollback()

{(CompleteHandle.WaitOne(0)); //Этот хэндл выставляется когда у транзакции уже очистились ресурсы, поэтому в продолжении нет смысла(_locker)

{(_commitFlag);

//throw new Exception("Не могу сделать Rollback, когда уже был вызван Commit");(_rollbackFlag && (WorkingThread.Current == null || !WorkingThread.Current.Equals(_myThread))); //Попытка повторно поставить в очередь откат

_rollbackFlag = true;

_myThread.OnIdle -= _myThread_OnIdle;

}(WorkingThread.Current != null && WorkingThread.Current.Equals(_myThread))

{(OnRollback != null)(this, null);

_cTrans.Rollback();

//добавляем задачу очистки ресурсов транзакции

_myThread.queue.Push(new WorkItem(new WorkItem.anywork(this.FreeResources)));

}

{.anywork w = () => { this.Rollback(); };wi = new WorkItem(w, null);(wi);.AsyncWaitHandle.WaitOne();

//_completeHandle.WaitOne();

}

}

/// <summary>

/// накат транзакции

/// </summary>

/// <remarks> если метод вызван из контекста транзакции или рабочего потока,

/// то в очередь транзакции помещается задание завершения транзакции и работа метода завершается

/// Если метод вызван вне контекста рабочих потоков,

/// то выполняется ожидание фактического завершения транзакции

/// </remarks>void Commit()

{(CompleteHandle.WaitOne(0)); //Этот хэндл выставляется когда у транзакции уже очистились ресурсы, поэтому в продолжении нет смысла

//сделать чтобы коммит или роллбэк был только один(_locker)

{(_lastException != null) //уже было какое-то исключениеnew InvalidOperationException("При выполнении транзакции возникло исключение", _lastException);(_rollbackFlag)

{;

//throw new Exception("Не могу вызвать Commit, так как уже был вызван Rollback");

}(_commitFlag && (WorkingThread.Current == null || !WorkingThread.Current.Equals(_myThread))); //Попытка повторно поставить в очередь накат

_commitFlag = true;

_myThread.OnIdle -= _myThread_OnIdle;

}(WorkingThread.Current != null && WorkingThread.Current.Equals(_myThread))

{(OnCommit != null)(this, null);

_connection.Commit();

_cTrans.Commit();

//Применяем изменения к файлам транзакции

//Перед этим должны быть созданы по идее резервные копии..(TransactionFile tfile in _transactionFiles.Values).Flush();

//добавляем задачу очистки ресурсов транзакции

_myThread.queue.Push(new WorkItem(new WorkItem.anywork(this.FreeResources)));

}

{wi = new WorkItem(new WorkItem.anywork(this.Commit));(wi);.AsyncWaitHandle.WaitOne();

// _completeHandle.WaitOne();

}

}

/// <summary>

/// добавляет задачу для выполнения в рамках транзакции

/// </summary>

/// <param name="wi">задача для выполнения</param>

/// <remarks>

/// Задача помещается в очередь задач потока и одновременно в очередь незавершенных задач

/// транзакции. После выполнения задачи она удаляется из очереди незавершенных задач транзакции.

///

/// При выполнении задачи в контекст устанавливается текущая транзакция

///

/// Если при выполнении задачи в транзакции возникло исключение - транзакция автоматически откатывается

/// </remarks>

/// <exception cref="InvalidOperationException">Транзакция находится в состоянии завершения и не может принимать новых задач</exception>void AddAction(Cognitive.Threading.WorkItem wi)

{(_locker)

{(_rollbackFlag || _commitFlag)new InvalidOperationException("Транзакция находится в состоянии завершения и не может принимать новых задач");(wi);

}

}void Run(Cognitive.Threading.WorkItem wi)

{(_locker)

{(_myThread.State == WorkingThreadStates.Stopping)

{ //Фиктивное завершение

(wi.AsyncWaitHandle as System.Threading.ManualResetEvent).Set();;

}.WorkException += new WorkItemExceptionDelegate(wi_WorkException);.WorkComplete += new WorkItemEventDelegate(wi_WorkComplete);.WorkActivating += new WorkItemEventDelegate(wi_WorkActivating);

_running = wi;

_myThread.queue.Push(_running);

}

}void EnqueueWorkItem(Cognitive.Threading.WorkItem wi)

{(wi == null)new NullReferenceException("wi не может быть null");(_locker)

{(_running == null)(wi); //ничего не выполняется - сразу запускаем

{(_myThread.State == WorkingThreadStates.Stopping)

{ //Фиктивное завершение

(wi.AsyncWaitHandle as System.Threading.ManualResetEvent).Set();;

}

_transactionQueue.Enqueue(wi); //кладем в очередь

}

}

}CWIAsync : IAsyncResult

{_wi;_Complete = false;CWIAsync(WorkItem wi)

{

_wi = wi;

_wi.WorkComplete += new WorkItemEventDelegate(_wi_WorkComplete);

}_wi_WorkComplete(WorkItem source)

{

_Complete = true;

}

#region IAsyncResult Membersobject AsyncState

{{ return null; }

}System.Threading.WaitHandle AsyncWaitHandle

{{_wi.AsyncWaitHandle;

}

}bool CompletedSynchronously

{{ return _Complete; }

}bool IsCompleted

}

#endregion

}Object Invoke(Delegate del, params object[] p)

{wi = null;(p.Length)

{0:= new WorkItem(del);;1:= new WorkItem(del, p[0]);;2:= new WorkItem(del, p[0], p[1]);;3:= new WorkItem(del, p[0], p[1], p[2]);;4:= new WorkItem(del, p[0], p[1], p[2], p[3]);;:new NotImplementedException();

}ex = null;we = (s, e) => { ex = e; };.WorkException += we;

/**/(wi);

/*EnqueueWorkItem(wi);*/.AsyncWaitHandle.WaitOne();(ex != null)new System.Reflection.TargetInvocationException(ex);wi.Result;

}IAsyncResult InvokeAsync(Delegate del, params object[] p)

{wi = null;(p.Length)

{0:= new WorkItem(del);;1:= new WorkItem(del, p[0]);;2:= new WorkItem(del, p[0], p[1]);;3:= new WorkItem(del, p[0], p[1], p[2]);;4:= new WorkItem(del, p[0], p[1], p[2], p[3]);;:new NotImplementedException();

}ar = new CWIAsync(wi);

/**/(wi);

/*EnqueueWorkItem(wi);*/ar;

}Microsoft.Samples.DbConnectionScope.DbConnectionScope _dbScope;

/// <summary>

/// обрабатываемый WorkItem

/// </summary>WorkItem CurrentWorkItem

{{(_locker)

{_running;

}

}

}

/// <summary>

/// попытка войти в режим блокировки

/// </summary>

/// <param name="lockId">идентификатор блокировки</param>

/// <returns></returns>bool TryEnterLock(String lockId)

{(!this.Equals(Transaction.Current))new InvalidOperationException("TryEnterLock может быть вызван только из потока транзакции");(Transaction.Current._server.blockMan.TryEnterBlock(lockId))true;false;

}bool IsInLock(String lockId)

{_server.blockMan.IsInLock(this, lockId);

}

/// <summary>

/// вход в режим блокировки с ожиданием

/// </summary>

/// <param name="lockId">идентификатор блокировки</param>void EnterLock(String lockId)

{(Transaction.Current != null)new InvalidOperationException("Для внесения блокировки заданием следует использовать TryEnterLock вместо EnterLock");(new CLockWorkItem(this, lockId));

}

/// <summary>

/// При выполнении задачи в контекст ее потока устанавливается текущая транзакция

/// </summary>

/// <param name="source"></param>wi_WorkActivating(WorkItem source)

{(_locker)

{(!_myThread.Context.ContainsKey("Transaction"))

_myThread.Context.Add("Transaction", this);

}

///Устанавливаем текущую транзакцию.Transactions.Transaction.Current = _cTrans;

_dbScope = new Microsoft.Samples.DbConnectionScope.DbConnectionScope();

_dbScope.AddConnection(_sqlConn.ConnectionString, _sqlConn);

}

/// <summary>

/// Выполнение задачи завершилось и она удаляется из очереди незавершенных задач транзакции

/// </summary>

/// <param name="source"></param>wi_WorkComplete(WorkItem source)

{(_locker)

{wi = _running;

//закрываем открытый scope соединения(_dbScope != null)

{

_dbScope.AddConnection(_sqlConn.ConnectionString, new SqlConnection());

_dbScope.Dispose();

_dbScope = null;

}

_MyIdleStart = DateTime.Now;

_running = null;(source is CLockWorkItem)

{(true != (bool)source.Result)

{(((CLockWorkItem)source).Copy());;

}

}

///вытаскиваем следующий элемент из очереди(_transactionQueue.Count > 0)(_transactionQueue.Dequeue());

}

}Exception _lastException;

/// <summary>

/// Откат, если при выполнении задачи в транзакции возникло исключение

/// </summary>

/// <param name="source"></param>

/// <param name="e"></param>wi_WorkException(WorkItem source, Exception e)

{.WriteLine("исключение в транзакции " + this._MyIndex.ToString());(_locker)

{

//Исключение не в коммите и не в роллбэке(!_commitFlag && !_rollbackFlag && _lastException == null)

_lastException = e;

_commitFlag = false; //Нельзя коммитить то, что ошиблось(_rollbackFlag)e; //падение при роллбэке обработать не можем

}

//Вызов происходит из рабочего потока, в его очередь ставится откат и, затем, освобождение

//ресурсов, которые сразу и выполняются();(_lastException != null);

//После этого можно кинуть исключениеe;

}

/// <summary>

/// создание файла в транзакции

/// </summary>TransactionFile CreateFile(string name)

{tf = new TransactionFile(name);(_locker)

{

_transactionFiles.Add(tf.id, tf);

}tf;

}

/// <summary>

/// возвращает созданный файл по его идентификатору

/// </summary>TransactionFile GetFile(string fileid)

{(_locker)

{(_transactionFiles.ContainsKey(fileid))_transactionFiles[fileid];null;

}

}

/// <summary>

/// возврашает текущую транзакцию

/// </summary>

/// <remarks>

/// возвращает текущую транзакцию из контекста выполнения

/// </remarks>static Transaction Current

{

{

{(WorkingThread.Current == null)null;WorkingThread.Current.Context["Transaction"] as Transaction;

}(NullReferenceException )

{null;

}(KeyNotFoundException )

{null;

}

}

}

#region IDFObjectsProvider Members

/// <summary>

/// выполняет конструирование объектов по идентификатору в контексте транзакции

/// </summary>

/// <param name="id">полный идентификатор объекта</param>

/// <returns>созданный объект</returns>

/// <remarks>

/// Этот метод выполняет конструирование и кэширование ранее запрошенных объектов в рамках транзакции

/// При завершении транзакции выполняется полная очистка кэша

/// </remarks>Environment.IDFObject CreateObject(string id)

{.IDFContainerObject root = _server.CreateRootObject(this._connection);(String.IsNullOrEmpty(id))root;

///разбор пути, конструирование и кэширование объектов[] ObjectKeys = id.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);.IDFContainerObject tempContObj = root;.IDFObject tempObj = null;(String CurrentKey in ObjectKeys)

{= tempContObj.GetChild(CurrentKey);(!(tempObj is Environment.IDFContainerObject))tempObj;= (tempObj as Environment.IDFContainerObject);

}tempObj;

}

#endregion

/// <summary>

/// возвращает соединение, связанное с транзакцией

/// </summary>CTNKDESMLib.ICTDEDatabase Connection

{{_connection;

}

}

/// <summary>

/// возвращает объект для управления взаимодействией в рамках транзакции

/// </summary>Interaction.Interaction Interaction

{

{new Interaction.Interaction(_connection);

}

}

/// <summary>

/// добавление объекта в конекст транзакции

/// </summary>

/// <param name="key"></param>

/// <param name="p"></param>void AddContextObject(String key, Object p)

{(_locker)

{(_ctx.ContainsKey(key))

_ctx[key] = p;

_ctx.Add(key, p);

}

}

/// <summary>

/// получение объекта из контекста транзакции

/// </summary>

/// <param name="key"></param>

/// <returns></returns>Object GetContextObject(String key)

{(_locker)

{(_ctx.ContainsKey(key))_ctx[key];return null;

}

}

}

/// <summary>

/// контекст транзакции

/// </summary>class TransactionContext : System.Collections.Generic.Dictionary<string, Object>

{

}class CLockWorkItem : WorkItem

{delegate bool TryEnterLockDelegate(String x);String _LockId;Transaction _Trans;CLockWorkItem(Transaction tr, String lockId)

:base(new TryEnterLockDelegate(tr.TryEnterLock), lockId)

{

_Trans = tr;

_LockId = lockId;

}WorkItem Copy()

{new CLockWorkItem(_Trans, _LockId);

}

}

}System;System.Collections.Generic;System.Linq;System.Text;System.IO;Cognitive.Docflow.Transactions

{

/// <summary>

/// Класс, описывающий файл, созданный в рамках транзакции

/// </summary>class TransactionFile : IDisposable

{String _id;FileStream _fs;Transaction _transaction;String _externalFullPath;String _internalFullPath;FileAccess _access;

/// <summary>

/// Копирует файл во временную директорию и работает с ним до завершения транзакции

/// </summary>

/// <param name="path">Полное имя файла (не пустое, если файл не существует, он будет создан после коммита)</param>TransactionFile(string path)

{(String.IsNullOrEmpty(path))new ArgumentException("Невозможно создать TransactionFile: полное имя файла не должно быть пустым.");(!Directory.Exists(Path.GetDirectoryName(path)))new ArgumentException(String.Format("Невозможно создать TransactionFile {0}: его путь не существует!", path));

_transaction = Transaction.Current;

_internalFullPath = Path.GetTempFileName();(File.Exists(path)).Copy(path, _internalFullPath, true);

_externalFullPath = path;

_id = _externalFullPath + "||" + Guid.NewGuid().ToString();

_access = FileAccess.Read;

}

/// <summary>

/// Чистка

/// </summary>

~TransactionFile()

{();

}

/// <summary>

/// Чистка

/// </summary>void Dispose()

{(_fs != null)

_fs.Dispose();(File.Exists(_internalFullPath)).Delete(_internalFullPath);

}

/// <summary>

/// идентификатор файла

/// </summary>String id {

{ return _id; }

}

/// <summary>

/// транзакция, к которой относится данный файл

/// </summary>Transaction transaction

{

{_transaction;

}

}

/// <summary>

/// возвращает поток для доступа к содержимому файла

/// </summary>

/// <param name="access"></param>

/// <returns></returns>FileStream GetStream(FileAccess access)

{(_fs == null || !(_fs.CanRead || _fs.CanWrite) || access != _access)

{(_fs != null)

_fs.Close();

_access = access;(!File.Exists(_internalFullPath) && File.Exists(_externalFullPath)).Copy(_externalFullPath, _internalFullPath);

_fs = File.Open(_internalFullPath, FileMode.OpenOrCreate, _access);

}_fs;

}

/// <summary>

/// закрывает открытый поток и сбрасываем содержимое на диск

/// (применяет к файлу все изменения)

/// </summary>void Flush()

{

///если производилась работа с потоком, то производим сброс изменений на диск(_fs != null)

_fs.Close();(!File.Exists(_internalFullPath));(!Directory.Exists(Path.GetDirectoryName(_externalFullPath)))new InvalidOperationException(String.Format("Невозможно записать содержимое TransactionFile на диск в файл {0}, т.к. пути его размещения не существует!!", _externalFullPath));.Copy(_internalFullPath, _externalFullPath, true);.Delete(_internalFullPath);

}

/// <summary>

/// возвращает полный путь к файлу

/// </summary>String FullPath { get { return _externalFullPath; } }

}

}System;System.Collections.Generic;System.Linq;System.Text;Cognitive.Docflow;Cognitive.Docflow.Environment

{

/// <summary>

/// серверная среда выполнения

/// </summary>abstract class EnvironmentServer

{.ConnectionPool _cpool = null;.Threading.ThreadPool _tpool = null;BlockManager _blockManager = null;object _locker = new object();bool _stopping = false;

/// <summary>

/// конструктор серверной среды выполнения

/// </summary>

/// <param name="sqlConnectionString"></param>EnvironmentServer(String sqlConnectionString)

{

//конструируем пул соединений

_cpool = new Transactions.ConnectionPool(sqlConnectionString, String.Empty, String.Empty);

//конструируем пул потоков

_tpool = new Cognitive.Threading.ThreadPool(4, TimeSpan.FromSeconds(1));

_blockManager = new BlockManager();

_tpool.OnIdle += new Cognitive.Threading.ThreadPoolEvent(ThreadPooll_OnIdle);

}

/// <summary>

/// конструктор серверной среды выполнения

/// </summary>

/// <param name="sqlConnectionString"></param>

/// <param name="threadsCount"></param>EnvironmentServer(String sqlConnectionString, int threadsCount)

{

//конструируем пул соединений

_cpool = new Transactions.ConnectionPool(sqlConnectionString, String.Empty, String.Empty);

//конструируем пул потоков

_tpool = new Cognitive.Threading.ThreadPool(threadsCount, TimeSpan.FromMinutes(5));

_blockManager = new BlockManager();

_tpool.OnIdle += new Cognitive.Threading.ThreadPoolEvent(ThreadPooll_OnIdle);

}BlockManager blockMan {{ return _blockManager; }

}static Func<bool> _IdleProc

= new Func<bool>(() =>

{(Transactions.Transaction.Current.TryEnterLock("IDLE"))Transactions.Transaction.Current.Interaction.Idle();false;

});static Func<EnvironmentServer, Delegate, int> _TransactionCall =Func<EnvironmentServer, Delegate, int>((server, d) =>

{.Transaction tr = server.OpenTransaction();.Invoke(d);.Commit();0;

}

);

/// <summary>

/// запуск выполнения фоновых задач в рамках отдельной транзакции

/// </summary>

/// <param name="source"></param>ThreadPooll_OnIdle(Cognitive.Threading.ThreadPool source)

{(_openTransactions)

{(_stopping);

}

_TransactionCall.BeginInvoke(this, _IdleProc, null, null);(_openTransactions)

{(Transactions.Transaction trans in _openTransactions)

{//Поиск транзакций слишком долго простаивающих и их откат, если они уже не откатываются и не накатываются(trans.MyIdleInterval > TimeSpan.FromMinutes(5)) //тайм-аут транзакции 5 минут.Rollback();

}

}

}

/// <summary>

/// запуск рабочих потоков среды выполнения

/// </summary>public void Run()

{

}

/// <summary>

/// остановка рабочих потоков среды выполнения

/// </summary>public void Stop()

{(Threading.WorkingThread.Current != null)new InvalidOperationException("Остановка сервера не может быть вызвана из рабочего потока");(_openTransactions)

{

_stopping = true;

}

///проверяет есть ли еще открытые транзакции<bool> HasOpenTrans = new Func<bool>(() =>

{(_openTransactions)

{_openTransactions.Count > 0;

}

});

///возвращает последнюю открытую транзакцию<Transactions.Transaction> CurrentTrans =Func<Transactions.Transaction>(() =>

{(_openTransactions)

{(_openTransactions.Count > 0)_openTransactions[_openTransactions.Count - 1];

}null;

});dtWaitFor = DateTime.Now + TimeSpan.FromMinutes(1);(HasOpenTrans() && (DateTime.Now < dtWaitFor)).Threading.Thread.Sleep(50);

///пока есть открытые транзакции - завершаем их(HasOpenTrans())

{.Transaction curTrans = CurrentTrans();(curTrans == null);.Rollback();.CompleteHandle.WaitOne();

}

_tpool.Stop();

}List<Transactions.Transaction> _openTransactions =List<Cognitive.Docflow.Transactions.Transaction>();

/// <summary>

/// открытие транзакции

/// </summary>

/// <returns>созданный и проинициализированный объект транзакции</returns>

/// <remarks>

/// Создает транзакцию и связывает её с рабочим потоком.

/// Все открытае транзакции запоминаются.

/// В случае принудительной остановки сервера происходит откат всех незавершенных транзакций

/// </remarks>public Transactions.Transaction OpenTransaction()

{.Transaction tr = Transactions.Transaction.CreateInstance(this, _cpool, _tpool);(_openTransactions)

{(_stopping)new InvalidOperationException("Нельзя создавать транзакции когда сервер находится в состоянии завершения");

_openTransactions.Add(tr);

}.Complete += new Cognitive.Docflow.Transactions.TransactionEventHandler(tr_Complete);tr;

}tr_Complete(Cognitive.Docflow.Transactions.Transaction sender, EventArgs e)

{(_openTransactions)

{

_openTransactions.Remove(sender);

}

}abstract IProtocolWriter GetProtocol(ActionPriority priority);

/// <summary>

/// конструирует корневой объект бизнес-логики

/// </summary>

/// <param name="datasource">соединение со средой хранения</param>

/// <returns></returns>abstract Environment.IDFContainerObject CreateRootObject(CTNKDESMLib.ICTDEDatabase datasource);

}

}

Похожие работы на - Проектирование и разработка подсистемы управления транзакциями для АСУД 'ЕВФРАТ'

 

Не нашли материал для своей работы?
Поможем написать уникальную работу
Без плагиата!