|
Программирование >> Разработка устойчивых систем
int mainO { cout Press <Enter> to quit endl: try { CountedPtr<ThreadLocal Variabl es> tlv(new ThreadLocalVariables): const int SZ = 5: ThreadedExecutor executor: for(int i = 0: i < SZ: i++) executor.execute(new Accessor(tlv. i)): Cin.getO: tlv->cancel(): Завершение всех задач Accessor } catch(Synchronization ExceptionS e) { cerr e.whatO endl: } III- При создании объекта ThreadLocal посредством специализации шаблона для обращения к содержимому объекта могут использоваться только функции get() и set(). Функция get() возвращает копию объекта, связанного с программным потоком, а функция set() подставляет свой аргумент в объект, ассоциированный с данным потоком, и возвращает прежний объект. Примеры применения этих функций встречаются в функциях increment() и get() класса ThreadLocalVariables. Так как объект tlv совместно используется несколькими объектами Accessor, он реализует интерфейс Cancelable, чтобы объектам Accessor можно было сообщить о завершении работы системы. Запуск этой программы наглядно показывает, что каждому программному потоку выделяется отдельный блок памяти для хранения данных. Завершение задач в предыдущих примерах для завершения задач использовались флаги или интерфейс Cancelable. Обычно этого бывает достаточно, но в некоторых ситуациях нужно, чтобы задачи завершались быстрее. В этом разделе будут рассматриваться особенности и потенциальные проблемы, возникающие при таком завершении. Начнем с программы, которая не только демонстрирует проблемы с ускоренным завершением, но и является дополнительным примером совместного использования ресурсов. Но прежде чем переходить к этому примеру, мы должны решить проблему коллизий в потоках ввода-вывода. CountedPtr<ThreadLocalVariаЫes> tl v: public: Accessor(CountedPtr<ThreadLocalVariables>& tl. int idn) : id(idn). tlv(tl) {} void runO { while(Itlv->isCanceled()) { tlv->increment(): cout *th1s endl: friend ostreamS operator (ostreamS os, AccessorS a) { return OS # a.id : a.tlv->get(): Предотвращение коллизий в потоках ввода-вывода Возможно, вы заметили, что в предыдущих примерах выводимые данные иногда искажались. Система потоков ввода-вывода С++ проектировалась без учета многопоточности, поэтому ничто не мешало результатам одного потока смешаться с результатами другого. А это означает, что при написании приложений необходимо синхронизировать операции ввода-вывода. Чтобы решить проблему, нужно сначала сгенерировать весь выходной пакет, а затем решить, когда отправить его на консоль. В одном из простых решений информация записывается в ostringstream, а затем один объект с мутексом синхронизирует вывод между всеми программными потоками: : Cll:Display.h Предотвращение коллизий при выводе #ifndef DISPLAY H #def1ne DISPLAY H linclude <iostream> linclude <sstream> linclude zthread/Mutex.h linclude zthread/Guard.h class Display { Один объект совместно используется ZThread::Mutex iolock: всеми потоками ввода-вывода, public: void outputCstd::ostringstream& os) { ZThread::Guard<ZThread::Mutex> g(iolock): std: :cout os.strO: lendif DISPLAY H /:- В этом варианте стандартные операторные функции operator () определены заранее, а объект строится в памяти с применением знакомых потоковых операторов. Когда задача хочет вывести сообщение, она создает временный объект ostringstream и использует его для построения нужного выходного сообщения. При вызове output() мутекс предотвращает запись в объект Display из нескольких программных потоков (как будет показано далее, в программе должен существовать только один объект Display). Этот пример всего лишь демонстрирует базовый принцип, но при желании его можно усовершенствовать. Например, чтобы обеспечить выполнение требования о наличии только одного объекта Display в программе, можно преобразовать его в Синглет (в библиотеку ZThread входит шаблон Singleton, предназначенный для реализации Синглетов). Подсчет посетителей в следующей программе требуется узнать, сколько посетителей ежедневно заходит в парк. На каждом проходе установлена вертушка или похожий механизм. После увеличения отдельного счетчика вращений увеличивается общий счетчик, представляющий суммарное количество посетителей парка. : СП: Ornamental Garden, срр {L} ZThread linclude <vector> class Entrance : public Runnable { CountedPtr<Count> count: CountedPtr<D1splay> display: int number: int id: bool waitingForCancel: public: Entrance(CountedPtr<Count>& cnt, CountedPtr<Display>& disp. int idn) : count(cnt). display(disp). id(idn). number(O), waitingForCancel(false) {} void runO { #1 nclude <cstdl1b> #i nclude <ctime> finclude Display.h finclude zthread/Thread.h finclude zthread/FastMutex.h finclude zthread/Guard.h finclude Zthread/ThreadedExecutor.h finclude zthread/CountedPtr.h using namespace ZThread: using namespace std: class Count : public Cancelable { FastMutex lock: int count: bool paused, canceled: public: CountO : count (0). paused(false), canceled(false) {} int incrementО { Если закомментировать следующую строку, подсчет перестает работать: Gua rd<Fa stMutex> g(1оск): int temp = count : if(rand() 2 == 0) Передача управления в половине случаев Thread::yield(): return (count = ++temp): int valueO { Guard<FastMutex> g(lock): return count: void cancel О { Guard<FastMutex> g(lock): canceled = true: bool IsCanceledO { Guard<FastMutex> g(lock): return canceled: bool pauseO { Guard<FastMutex> g(lock): paused = true: bool isPausedO { Guard<FastMutex> g(lock): return paused:
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |