Визуальное программирование и MFC

       

Работа с семафорами


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

CSemaphore( LONG lInitialCount = 1, LONG lMaxCount = 1, LPCTSTR pstrName = NULL, LPSECURITY_ATTRIBUTES lpsaAttributes = NULL );

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

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

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

Последний параметр конструктора является указателем на набор атрибутов прав доступа, связанный с семафором. Если этот параметр равен NULL, то семафор наследует данный набор у вызвавшего его потока.

Внесем некоторые изменения в пример, рассматриваемый в предыдущем параграфе данной главы.
Добавим в класс CExampleView объект-семафор:

class CExampleView : public CView { protected: // только один поток сможет одновременно обращаться к ресурсу CSemaphore sem; // другие описания класса ..... };

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

UINT MyThread1(LPVOID pParam); UINT MyThread2(LPVOID pParam); void CExampleView::OnStart() { AfxBeginThread(MyThread1,this); AfxBeginThread(MyThread2,this; } UINT MyThread1(LPVOID pParam) { CExampleView *ptrView=(CExampleView *)pParam; CSingleLock syncObj(&(ptrView->sem)); ....... syncObj.Lock(); // получение семафора действия, связанные с доступом к ресурсу syncObj.Unlock(); // освобождение семафора ....... return 0; } UINT MyThread2(LPVOID pParam) { CExampleView *ptrView=(CExampleView *)pParam; CSingleLock syncObj(&(ptrView->sem)); ....... syncObj.Lock(); // получение семафора действия, связанные с доступом к ресурсу syncObj.Unlock(); // освобождение семафора ....... return 0; }

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


Содержание раздела