Задача следующая: создать n потоков и в каждом из них выводить уникальную строку посимвольно. Операция вывода должна быть атомарной. Вот функция потока:
DWORD WINAPI printUniqueString(PVOID args)
{
ThreadArgs* ta = reinterpret_cast<ThreadArgs*>(args);
string str = "Thread";
while (!ta->canClose)
{
EnterCriticalSection(&this->section);
for (int i = 0; i < str.length(); i++)
{
cout << str[i];
Sleep(200);
}
cout << ta->number << endl;
LeaveCriticalSection(&this->section);
Sleep(200); // без этой строчки не работает
}
delete ta;
return 0;
}
Проблема кроется в том, что без использования функции Sleep объект критической секции сразу же после освобождения занимается тем же потоком вновь, и другие потоки не могут получить доступ к секции. Полагаю, что эта проблема слишком распространенная, чтобы иметь красивое лаконичное решение. Кто-то имел с ней дело?
Как выкрутиться из этой ситуации, не используя Sleep?
Во-первых, критическая секция - объект внутрипроцессорной синхронизации, которая может использоваться только для синхронизации разных потоков внутри одного процесса. В вашем случае все потоки должен создавать один процесс, я исхожу из того, что у Вас это условие выполняется.
Во-вторых, переключение потоков по-таймауту - достаточно медленная операция, которая выполняется редко (относительно, конечно). Поэтому маловероятно, что система будет делать такое переключение как раз между Вашими LeaveCriticalSection и последующим EnterCriticalSection в следующей итерации цикла - этот фрагмент кода выполняется очень быстро, буквально в пару машинных инструкций.
Чтобы подсказать системе, как надо действовать, Вы можете попробовать вызвать WINAPI функцию BOOL SwitchToThread(VOID) в конце тела цикла перед переходом на следующую итерацию. В принципе, Вашим Sleep(200) Вы делаете то же самое, только дополнительно устанавливаете таймаут в 200 мсек, в течение которого просите систему не возвращать управление в этот поток.
Правда, следует учитывать, что SwitchToThread() - необязательна для системы, она может просто ее проигнорировать, если у нее нет потоков, ожидающих передачи управления.
Вообще, У Вас в данном примере слишком жесткая конкуренция за разделяемый ресурс - и отсюда все проблемы: все потоки на каждые N*200 мсек. обладания критической секцией (где N - количество символов в строке) дают только пару машинных команд (если убрать Sleep, который после LeaveCriticalSection) - считайте миллионные доли секунды (если не миллиардные) другим потокам на возможность захватить секцию. А в принципе критические секции - как раз наоборот - объект синхронизации, который себя оправдывает при низкой конкуренции за ресурс, когда 99% времени ресурс не нужен и только на 1% мы его захватываем и как можно быстрее освобождаем.
Попробуйте воспользоваться вызовом
std::this_thread::yield();
(thrd_yield() в чистом C).
Сборка персонального компьютера от Artline: умный выбор для современных пользователей