Процессы в Windows
Страница 12. Критические секции


Критические секции. Синхронизация в пользовательском режиме

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

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

typedef ... ItemData;

// Элемент очереди: данные и два указателя на предыдущий и следующий элементы
typedef struct _ItemStruct
{
ItemData data;
struct _ItemStruct * prev,*next;
} Item;

// Описание класса "Очередь"
class CMyQueue
{
CRITICAL_SECTION m_crisec; // Критическая секция
Item * m_Begin; // Указатель на первый элемент
Item * m_End; // Указатель на последний элемент
int m_Count; // Количество элементов

public:
CMyQueue()
{
// Инициализируем критическую секцию
InitializeCriticalSection(&m_crisec);
// Инициализируем переменные
m_Count = 0;
m_Begin = m_End = NULL;
}

~CMyQueue()
{
// Удаляем все элементы очереди
while(m_Count) GetItem();
// Удаляем критическую секцию
DeleteCriticalSection(&m_crisec);
}

void AddItem(ItemData data)
{
Item * NewItem;
Item * OldFirstItem;
NewItem = new Item(); // New item
NewItem->next = NULL;
NewItem->prev = NULL;
NewItem->data = data;
// ------------------------ Этот кусок не может выполняться параллельно
EnterCriticalSection(&m_crisec); // Заходим в к.с. (ждем входа)
OldFirstItem = m_Begin;
m_Begin = NewItem;
NewItem->next = OldFirstItem;
if (OldFirstItem)
OldFirstItem->prev = NewItem;
else
m_End = NewItem;
m_Count++;
LeaveCriticalSection(&m_crisec); // Выходим из к.с.
// ------------------------ Этот кусок не может выполняться параллельно
}

ItemData GetItem()
{
ItemData data;
// ------------------------ Этот кусок не может выполняться параллельно
EnterCriticalSection(&m_crisec); // Заходим в к.с. (ждем входа)
if (!m_End)
data = NULL;
else
{
data = m_End->data ;
if (m_End->prev )
{
m_End->prev ->next = NULL;
m_End = m_End->prev ;
}
else
{
m_End = NULL;
m_Begin = NULL;
}
m_Count --;
}
LeaveCriticalSection(&m_crisec); // Выходим из к.с.
// ------------------------ Этот кусок не может выполняться параллельно
return data;
};
};
 
« Предыдущая статья   Следующая статья »