Воскресенье, 19.05.2024, 04:18
Приветствую Вас Guest Member

Windows XP / 7 .

[ Новые сообщения · Участники · Правила форума · Поиск · RSS ]
  • Страница 1 из 2
  • 1
  • 2
  • »
Архив - только для чтения
Форум <<Помощь по компьютерам>> » Низкоуровневое программирование » Исходные коды » Исходные коды: C# (Исходные коды: C#)
Исходные коды: C#
MafiozikOДата: Суббота, 22.08.2009, 23:32 | Сообщение # 1
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
Классы
GsvActiveObject

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


Админ
 
MafiozikOДата: Суббота, 22.08.2009, 23:32 | Сообщение # 2
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
Под параллелизмом в статье понимается только программно реализованный параллелизм, а фактически, его имитация на одном процессоре. Из рассмотрения исключаются специфические системы, связанные с аппаратурной поддержкой, например, массовый (векторный) параллелизм или "тонкий" параллелизм на уровне операторов (как это делается в транспьютерных системах и в таких языках, как Оккам [6]). Традиционная реализация параллелизма основана на функциональной декомпозиции, то есть, единицей параллельности является процедура (функция нити или сопрограмма). Рассматриваемая реализация основана на объектной декомпозиции и известна под названиями "живой объект", "активный объект", "агент". Суть этой концепции состоит в том, что объект, с одной стороны, является полноценным объектом (в терминах объектно-ориентированного подхода), а с другой стороны, он проявляет свою активность независимо от активности других объектов. Если в последовательной программе можно выделить только один поток управления, то в параллельной программе существует множество потоков управления, причем каждый активный объект живет своей особой жизнью.

Поскольку одним из критериев выбрана независимость от операционной системы, то активные объекты не базируются на объектах ядра. Учет другого условия, независимости от языка программирования, исключает возможность использования сопрограмм. Рассмотрим более подробно особенности сопрограмм, которые требуют поддержки со стороны компилятора. Во-первых, это необходимость "мягкого" возврата из подпрограммы. Под этим термином понимается возможность возобновления подпрограммы с той точки, в которой она была прервана оператором возврата (в англоязычной литературе используется термин continuation - продолжение). Такой возврат существенно отличается от обычного возврата из подпрограммы, при котором локальный контекст полностью теряется и стек остается в том же состоянии, в каком он был до вызова подпрограммы. Для сопрограммы требуется сохранить как локальный контекст, так и стек. Частично задачу можно решить, если сопрограмму заменить объектом, который будет сохранять локальный контекст. Но проблема стека (а точнее, его неопределенного размера) остается, и избежать ее можно только, если исключить "мягкий" возврат и восстанавливать стек в том же состоянии, что и при вызове.

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

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


Админ
 
MafiozikOДата: Суббота, 22.08.2009, 23:33 | Сообщение # 3
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
Скелет активного объекта

Code
public class ActiveObject
{
   protected virtual void Step() { }

   public bool Active { get{} set{} }
   public bool Sleeping { get{} }
   public void Sleep(int time) { }
   public void Sleep() { }
   public void WakeUp() { }

   public static void Open(ThreadPriority priority) { }
   public static void Close() { }
}


Админ
 
MafiozikOДата: Суббота, 22.08.2009, 23:33 | Сообщение # 4
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
В приведенном коде выделяются три группы методов:

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

В целом, функциональность активного объекта соответствует функциональности нити или класса Thread. Объект может быть активным или неактивным. В активном состоянии его жизненный цикл определяется методом Step. Установка свойства Active изменяет активность объекта. Методы Sleep деактивируют объект на указанный интервал времени или до вызова WakeUp. Свойство Sleeping возвращает истинное значение, если объект находится в состоянии ожидания. Диспетчеризацию (циклический обход всех активных объектов и вызов у каждого активного объекта метода Step), инициирует статический метод Open. Он открывает театр жизненных драм всех активных объектов приложения, а метод Close его закрывает.


Админ
 
MafiozikOДата: Суббота, 22.08.2009, 23:33 | Сообщение # 5
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
Метод Step

Как уже было сказано, активный объект реализуется как конечный автомат. Простейшая, классическая реализация автомата использует целочисленную переменную состояния (state) и оператора выбора (switch).

Code

public class SampleActiveObject : ActiveObject
{
   private int state = 0;

   protected override void Step()
   {
     switch (state)
     {
       case 0:
         // действия 0
         break;
       case N:
         // действия N
         break;
     }
   }
}


Админ
 
MafiozikOДата: Суббота, 22.08.2009, 23:33 | Сообщение # 6
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
При вызове метода Step объект выполняет действие, которое либо изменяет состояние переменной state, либо оставляет ее в неизменном состоянии, после чего метод завершается. Такая реализация в высшей степени эффективна и переносима. Однако она весьма трудоемка при большом числе состояний автомата. Для преодоления этого недостатка было предложено множество решений на основе паттернов State или State Machine, которые подробно описаны в литературе, например [3], [4]. Тема конечных автоматов поистине безбрежна, но, тем не менее, рискну добавить в это море еще пару своих решений, ориентированных на язык C#.
Использование делегатов

Основная идея состоит в том, что каждое состояние автомата описывается методом-делегатом, причем смена состояния приводит к смене делегата.

Code
public class SampleActiveObject : ActiveObject
{
   private delegate void State();
   private State state;

   private State state0;
   private State stateN;

   private void State0()
   {
     // действие 0
   }

   private void StateN()
   {
     // действие N
   }

   public SampleActiveObject()
   {
     state0 = new State(State0);
     stateN = new State(StateN);

     state = state0;
   }

   protected override void Step()
   {
     state();
   }
}


Админ
 
MafiozikOДата: Суббота, 22.08.2009, 23:34 | Сообщение # 7
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
Смена состояния реализуется присваиванием переменной state нового делегата. В отличие от классической реализации, добавление нового состояния сводится к добавлению нового делегата, подобно тому, как в паттерне State добавляется новый объект-состояние.
Использование yield return

Во второй версии C# появился весьма удобный метод реализации итераторов [5]. Оператор yield return выполняет мягкий возврат очередного значения итератора, подобно тому, как это происходит в сопрограммах, но реализуется он через конечный автомат. Заглянув в код, генерируемый компилятором, можно увидеть, что компилятор создает скрытый класс, реализующий интерфейс IEnumerator, целочисленное поле state и метод MoveNext, содержащий табличный оператор switch. То есть, реализация yield в точности соответствует классическому конечному автомату. Поскольку оператор yield return можно применять только в итераторах, потребуется немного изменить сигнатуру метода Step.

Code
public class SampleActiveObject : ActiveObject
{
   protected override IEnumerator Step()
   {
     while (true)
     {
       // действие 0
       yield return null;
       // действие N
       yield return null;
     }
   }
}


Админ
 
MafiozikOДата: Суббота, 22.08.2009, 23:34 | Сообщение # 8
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
Поскольку yield return не используется для возврата текущего состояния итератора, нам не важно возвращаемое значение. Оператор yield return завершает выполнение шага и приводит к выходу из метода Step. Фактически, мы предоставляем компилятору реализацию конечного автомата. Естественно, что метод Step может содержать различные условные и циклические конструкции и yield return может завершать выполнение метода в той или иной точке. Бесконечный цикл нужен в том случае, если жизнь активного объекта не заканчивается на последнем операторе yield return. В этом коде конечный автомат совсем не виден, но зато становится явной сопрограммная ориентация без явного выделения состояния. Поскольку иногда удобно использовать автоматную реализацию, а иногда сопрограммную, то введем в определение активного объекта оба варианта методов.

Code
private IEnumerator enumerator;

protected virtual IEnumerator Life()
{
   throw new NotImplementedException();
}

protected virtual void Step()
{
   if (enumerator == null)
     enumerator = Life();
   if (!enumerator.MoveNext())
   {
     Active = false;
     enumerator = null;
   }
}


Админ
 
MafiozikOДата: Суббота, 22.08.2009, 23:34 | Сообщение # 9
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
Если жизнь активного объекта более естественно выражается в терминах конечного автомата, то наследуемый класс будет переопределять метод Step, если же более удобна сопрограммная реализация, то наследуемый класс будет переопределять метод Life. В последнем случае метод Step переадресует управление методу Life явным получением и вызовом итератора. Когда MoveNext возвращает значение false, это означает, что жизненный цикл активного объекта завершился и нужно выполнить его деактивацию. Наследуемый класс должен обязательно переопределить один из методов, в противном случае при активации объекта будет возбуждена исключительная ситуация. Особо можно отметить, что итератор (enumerator) инициализируется в методе Step, а не в конструкторе объекта. Это нужно, чтобы иметь возможность создания объекта через System.Activator.CreateInstance и устранить возбуждение исключения при создании объекта. При завершении жизненного цикла активного объекта итератор уничтожается (ему присваивается нулевая ссылка) - это дает возможность помещать объекты в кэш и использовать их повторно.
Управление активностью объекта

Часть класса, которая управляет активностью объекта, довольно проста.

Code
private static Dispatcher dispatcher;

private ActiveObject prev;
private ActiveObject next;
private int timeout;
private bool sleeping;

public bool Active
{
   get
   {
     return next != null;
   }
   set
   {
     if (value)
       dispatcher.Add(this);
     else
       dispatcher.Remove(this);
   }
}

public bool Sleeping
{
   get
   {
     return sleeping;
   }
}

public void Sleep(int interval)
{
   dispatcher.Sleep(this, Environment.TickCount + interval);
}

public void Sleep()
{
   dispatcher.Sleep(this);
}

public void WakeUp()
{
   dispatcher.WakeUp(this);
}


Админ
 
MafiozikOДата: Суббота, 22.08.2009, 23:35 | Сообщение # 10
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
В последующих разделах статьи детали этого кода будут объяснены подробнее, а пока можно сказать, что класс активного объекта имеет скрытый статический объект - диспетчер, который содержит список объектов, находящихся в активном состоянии. Всем объектам из списка диспетчера периодически выделяется процессорное время - вызывается их метод Step. Каждый объект имеет два поля - ссылку на предыдущий активный объект (prev) и ссылку на следующий активный объект (next). Если объект не содержится в списке диспетчера (не имеет соседа), он находится в неактивном состоянии. Активация объекта заключается в его добавлении в конец списка диспетчера, а деактивация - в удалении из списка диспетчера.
Методы Open и Close

Code
private static Activator activator;  
private static Timer timer;  
private static Log log;

public static void Open(ThreadPriority priority)
{
   activator.Start(priority);
}

public static void Close()
{
   activator.Stop();
   dispatcher.Clear();
   timer.Clear();
   log.Clear();
}


Админ
 
MafiozikOДата: Суббота, 22.08.2009, 23:35 | Сообщение # 11
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
Метод Open создает и стартует нить, вызывая метод Start у скрытого статического объекта activator, а метод Close останавливает и уничтожает нить, а также очищает содержимое остальных скрытых статических объектов.

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

static ActiveObject()
{
dispatcher = new Dispatcher();
timer = new Timer();
activator = new Activator();
log = new Log();
}


Админ
 
MafiozikOДата: Суббота, 22.08.2009, 23:35 | Сообщение # 12
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
Диспетчер

Диспетчер представляет собой список объектов, находящихся в активном состоянии. Реализация диспетчера основана на двусвязном списке с явно выделенными граничными узлами first и last. Связи активного объекта с предыдущим и последующим хранятся в приватных полях prev и next. Некоторая сложность списка обусловлена тем, что активный объект может во время своего выполнения создать новый активный объект, активировать или деактивировать другой объект, или деактивировать самого себя. То есть, состояние списка может измениться при его обходе.


Админ
 
MafiozikOДата: Суббота, 22.08.2009, 23:36 | Сообщение # 13
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
Code
private class Dispatcher
{
   private ActiveObject first;       // голова списка
   private ActiveObject last;        // хвост списка
   private ActiveObject current;     // текущий объект при итерации
   private ActiveObject currentPrev; // предыдущий объект при итерации
   private int count;                // количество объектов в списке

   private void InitList()
   {
     first.next = last;
     last.prev = first;
     current = currentPrev = null;
     count = 0;
   }

   public Dispatcher()
   {
     first = new ActiveObject();
     last = new ActiveObject();
     InitList();
   }

   public void Add(ActiveObject obj)
   {
     lock (this)
     {
       if (obj.next == null && obj.exception == null && !obj.sleeping)
       {
         obj.prev = last.prev;
         obj.next = last;
         obj.prev.next = last.prev = obj;
         ++count;
       }
     }
   }

   public void Remove(ActiveObject obj)
   {
     lock (this)
     {
       if (obj.next != null)
       {
         if (obj == currentPrev)
           currentPrev = obj.prev;
         obj.prev.next = obj.next;
         obj.next.prev = obj.prev;
         obj.next = obj.prev = null;
         --count;
       }
     }
   }

   public int Count
   {
     get
     {
       return count;
     }
   }

   public ActiveObject GetFirst()
   {
     lock (this)
     {
       current = first.next;
       currentPrev = null;
       if (current == last) // список пуст
         current = null;
       return current;
     }
   }

   


Админ
 
MafiozikOДата: Суббота, 22.08.2009, 23:37 | Сообщение # 14
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
Code

public ActiveObject GetNext()
   {
     lock (this)
     {
       if (current != null)
       {
         current = current.next;
         if (current == null) // текущий объект был удален из списка
         {
           if (currentPrev == null)
             current = first.next;
           else
             current = currentPrev.next;
         }
         if (current == last) // достигнут конец списка
           current = currentPrev = null;
         else if (current != null)
           currentPrev = current.prev;
       }
       return current;
     }
   }

   public void Clear()
   {
     lock (this)
     {
       current = first.next;
       while (current != last)
       {
         ActiveObject next = current.next;
         current.prev = current.next = null;
         current = next;
       }
       InitList();
     }
   }



Админ
 
MafiozikOДата: Суббота, 22.08.2009, 23:38 | Сообщение # 15
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
Code

   public void Sleep(ActiveObject obj, int timeout)
   {
     bool ok = false;
     lock (this)
     {
       if (obj.timeout == 0 && !obj.sleeping && obj.exception == null)
       {
         obj.timeout = timeout;
         obj.sleeping = true;
         Remove(obj);
         ok = true;
       }
     }
     if (ok)
       timer.Add(obj);
   }

   public void Sleep(ActiveObject obj)
   {
     lock (this)
     {
       if (!obj.sleeping && obj.exception == null)
       {
         obj.sleeping = true;
         Remove(obj);
       }
     }
   }

   public void WakeUpByTimer(ActiveObject obj)
   {
     lock (this)
     {
       if (obj.timeout != 0 && obj.sleeping)
       {
         obj.timeout = 0;
         obj.sleeping = false;
         Add(obj);
       }
     }
   }

   public void WakeUp(ActiveObject obj)
   {
     lock (this)
     {
       if (obj.timeout == 0 && obj.sleeping)
       {
         obj.sleeping = false;
         Add(obj);
       }
     }
   }
}


Админ
 
Форум <<Помощь по компьютерам>> » Низкоуровневое программирование » Исходные коды » Исходные коды: C# (Исходные коды: C#)
  • Страница 1 из 2
  • 1
  • 2
  • »
Поиск: