Критические секции WinAPI в цикле: проблемы освобождения секции

202
22 апреля 2018, 23:00

Задача следующая: создать 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?

Answer 1

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

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

Чтобы подсказать системе, как надо действовать, Вы можете попробовать вызвать WINAPI функцию BOOL SwitchToThread(VOID) в конце тела цикла перед переходом на следующую итерацию. В принципе, Вашим Sleep(200) Вы делаете то же самое, только дополнительно устанавливаете таймаут в 200 мсек, в течение которого просите систему не возвращать управление в этот поток.

Правда, следует учитывать, что SwitchToThread() - необязательна для системы, она может просто ее проигнорировать, если у нее нет потоков, ожидающих передачи управления.

Вообще, У Вас в данном примере слишком жесткая конкуренция за разделяемый ресурс - и отсюда все проблемы: все потоки на каждые N*200 мсек. обладания критической секцией (где N - количество символов в строке) дают только пару машинных команд (если убрать Sleep, который после LeaveCriticalSection) - считайте миллионные доли секунды (если не миллиардные) другим потокам на возможность захватить секцию. А в принципе критические секции - как раз наоборот - объект синхронизации, который себя оправдывает при низкой конкуренции за ресурс, когда 99% времени ресурс не нужен и только на 1% мы его захватываем и как можно быстрее освобождаем.

Answer 2

Попробуйте воспользоваться вызовом

std::this_thread::yield();

(thrd_yield() в чистом C).

READ ALSO
Зависящие от времени действия в loopback

Зависящие от времени действия в loopback

Пытаюсь разобраться в loopback-4Создание Rest-API я освоил, теперь встала задача выполнять на сервере различные действия каждые несколько секунд...

175
Почему не применяется стиль к классу Angular 5

Почему не применяется стиль к классу Angular 5

Введено слово находим в тексте с помощью регулярного выражения и вкладиваем в <span class = "match"> слово </ span> с помощью пайпаСпан и класс приминяеться,...

183
return срабатывает раньше img.onload? [дубликат]

return срабатывает раньше img.onload? [дубликат]

На данный вопрос уже ответили:

254