Консультация № 187673
14.12.2013, 03:39
95.44 руб.
0 3 1
Предположим, у нас имеется единственное средство синхронизации - Event.
Предложите собственную реализацию критической секции в виде следующего класса (приводится только интерфейсная часть, private-члены - на ваше усмотрение):

class CriticalSection
{
public:
CriticalSection();
~CriticalSection();

void Lock();
void Unlock();
};

Обсуждение

Неизвестный
16.12.2013, 10:57
общий
Что-то у меня не получилось на одном классе и Event сделать универсальную критическую сессию. WaitForSingleObject срабатывает сразу у всех ждущих потоков, когда основной поток делает SetEvent. В чём тут дело, не понял.
Если никто не ответит, вернусь к задаче позже, пните меня, если что
Неизвестный
19.12.2013, 23:14
общий
19.12.2013, 23:15
Цитата: 181465
Что-то у меня не получилось на одном классе и Event сделать универсальную критическую сессию. WaitForSingleObject срабатывает сразу у всех ждущих потоков, когда основной поток делает SetEvent. В чём тут дело, не понял.
Если никто не ответит, вернусь к задаче позже, пните меня, если что


Если не сложно, отправьте, что получилось:)
Неизвестный
20.12.2013, 07:49
общий
это ответ
Здравствуйте, Козлов Олег Николаевич!

Кривое решение, но обо всём по порядку.

1. я сделал установку и снятие критической секции(КС) в конструкторе и деструкторе класса CCriticalSection соотвественно. Стало быть, чтобы зайти в КС достаточно объявить экземпляр класса с уникальным именем KC("MyEvent" в коде) в параметрах конструктора, а чтобы выйти, нужно чтобы сработал деструктор (как вызвать деструктор в нужном месте показано ниже, на примере С4 в функции main).
Почему именно так? Во-первых, тогда не надо явно звать Unlock. Во-вторых, это удобно в программах со сложной логикой, где может быть следующая ситуация: открыли КС, а потом по условию вышли из функции, а КС закрыть забыли. Или слишком много условных выходов, и в каждом условии надо было бы не забывать ставить Unlock() перед return. Данная конструкция избавляет нас от лишнего кода и защищает от ошибок и dead-lock-а.
1.1. тут есть один очень кривой момент - когда удалять handle? Мы заранее не знаем, кто последний из потоков будет использовать данную КС(и handle для Event внутри). Я завёл отдельный метод, который нужно звать, когда КС уже более не нужна.

2. CCriticalSection - класс КС. Методы Lock и Unlock зовуться в конструкторе и деструкторе соотвественно.
Метод Lock сначала пытается открыть Event по имени, если у него не получается (нет такого Event) он создаёт его.
Фукнция CreateEvent во втором аргументе использует FALSE - это означает, что состояние Event будет сбрасываться(освобождаться) автоматически.
Если поток не создаёт новый Event, то пытаеся получить управление над уже созданным (функция WaitForSingleObject(m_hEvent, INFINITE)), если на этом месте Event уже занят, поток впадает в ожидаение, останавливается на вызове WaitForSingleObject.
После того, как поток полочил Event в управление, он выводит сообщение об этом и выходит. После того, как поток выходит, Event автоматически освобождается, и кто-то из потоков, ждущий на WaitForSingleObject, получит управление и продолжит свою работу.

3. Главный поток (функция main) входит в КС (CCriticalSection C4("MyEvent")) и создаёт массив потоков (CreateThread) и запоминает в массив(HANDLE hThreads[TCOUNT]). КС в данном случае создана внутри локальной зоны видимости (скобки {// 1 ... }//1), и как только выполнение выйдет за скобки, сработает деструктор C4::~CCriticalSection, который вызовет Unlock и освободит Event, и main выйдет из КС.
3.1 Ещё одно кривое место - мне пришлось использовать напрямую функцию WaitForMultipleObjects для ожидания, пока все потоки закончатся. Я бы мог использовать нашу КС, но тогда main мог захватить КС раньше других потоков, и вся программа закончилась раньше, чем потоки отработали.
3.2 main в конце создаёт ещё одну КС и зовёт CloseHandle(), так как КС больше не будет использоваться.

Проект сделан в среде MS Visual Studio 6.0.

Приложение:
//#include "stdafx.h"
#include <Windows.h>
#include <iostream.h>

class CCriticalSection
{
DWORD m_dwOwner;
HANDLE m_hEvent;
public:
CCriticalSection(char *pszName);
virtual ~CCriticalSection();

protected:
void Lock(char *pszName);
void Unlock();
public:
void CloseHandle();
};

//-------------------------------
CCriticalSection::CCriticalSection(char *pszName)
{
m_dwOwner = NULL;
m_hEvent = NULL;
Lock(pszName);
}
CCriticalSection::~CCriticalSection()
{
Unlock();
}

//-------------------------------
void CCriticalSection::Lock(char *pszName)
{
m_hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, pszName);
if(!m_hEvent)
{
m_dwOwner = GetCurrentThreadId();
m_hEvent = CreateEvent(NULL, FALSE, FALSE, pszName);
cout << "Thread " << m_dwOwner << " creates Event\n";
}
else
{
DWORD dwWait = WaitForSingleObject(m_hEvent, INFINITE);
switch (dwWait)
{
case WAIT_OBJECT_0:
cout << "Thread " << GetCurrentThreadId() << " gets ownership\n";
break;

default:
cout << "Wait error (" << GetLastError() << ")\n";
return;
}
}
if(!m_hEvent)
{
cout << "Event fails\n";
}
}
void CCriticalSection::Unlock()
{
cout << "Thread " << GetCurrentThreadId() << " releases ownership\n";
SetEvent(m_hEvent);
}

void CCriticalSection::CloseHandle()
{
::CloseHandle(m_hEvent);
m_hEvent = NULL;
}

DWORD WINAPI ThreadProc(LPVOID lpParam)
{
CCriticalSection C("MyEvent");
cout << "Thread "<<GetCurrentThreadId()<<" exits\n";
return 1;
}

#define TCOUNT (7)
int main(int argc, char* argv[])
{
HANDLE hThreads[TCOUNT] = {0};
DWORD dwThreadID;
{// 1
CCriticalSection C4("MyEvent");
for(int i = 0; i < TCOUNT; i++)
{
hThreads[i] = CreateThread(NULL, 0, ThreadProc, NULL, 0, &dwThreadID);
if (hThreads[i] == NULL)
{
cout << "CreateThread failed ("<<GetLastError()<<")\n";
return 1;
}
}
}// 1
cout << "Main Thread "<< GetCurrentThreadId()<<" is waiting\n";
WaitForMultipleObjects(TCOUNT, hThreads, TRUE, INFINITE);
cout << "Main Thread "<< GetCurrentThreadId()<<" exits\n";

CCriticalSection C("MyEvent");
C.CloseHandle();

return 0;
}
5
Форма ответа