如何将临界区传递给另一个线程?

How to pass Critical Section to another thread?

我有 3 个线程,同时恢复,用不同的参数调用同一个函数。 如何强制线程离开临界区并将其传递给另一个线程?

当我运行下面的代码时,while循环被调用了很多次,直到另一个线程进入临界区(它也循环了很多次)。

DWORD WINAPI ClientThread(LPVOID lpParam)
{
    // thread logic 
    while(true)
    {
        EnterCriticalSection(&critical);
        // thread logic 
        LeaveCriticalSection(&critical);
        Sleep(0);
    }
    // thread logic 
    return 0;
}

换句话说,如何防止线程立即再次进入某个部分?

您不能直接要求线程离开临界区。线程将在完成执行后离开它。

所以唯一的办法就是阻止它进入临界区,或者“要求”它提前完成。例如。通过在该部分中连续检查 atomic_flag 并在检查过时中止停止线程的操作。

如果你想阻止一个线程离开后直接重新进入一个部分,你可以yield它,这将重新安排线程的执行。 如果你想从线程(A->B->C->D->A->B ...)中进行精确排序,你需要编写一个自定义调度程序或一个自定义“fair_mutex”来检测其他等待线程。

编辑:
这样的函数将是 BOOL SwitchToThread(); doc

正如另一个答案中提到的,您需要 Fair Mutex,而 Ticket Lock 可能是实现它的一种方法。

还有一种方法,基于二进制信号量,它实际上接近于以前的临界区。像这样:

class old_cs
{
public:
  old_cs()
  {
     event = CreateEvent(NULL, /* bManualReset = */ FALSE, /* bInitialState =*/ TRUE, NULL);
     if (event == NULL) throw std::runtime_error("out of resources");
  }

  ~old_cs()
  {
     CloseHandle(event);
  }

  void lock()
  {
    if (count.fetch_add(1, std::memory_order_acquire) > 0)
      WaitForSingleObject(event, INFINITE);
  }

  void unlock()
  {
    if (count.fetch_sub(1, std::memory_order_release) > 1)
      SetEvent(event);
  }

  old_cs(const old_cs&) = delete;
  old_cs(old_cs&&) = delete;
  old_cs& operator=(const old_cs&) = delete;
  old_cs& operator=(old_cs&&) = delete;
private:
  HANDLE event;
  std::atomic<std::size_t> count = 0;
};

您可能会在 Critical Section Objects documentation 中找到以下内容:

Starting with Windows Server 2003 with Service Pack 1 (SP1), threads waiting on a critical section do not acquire the critical section on a first-come, first-serve basis. This change increases performance significantly for most code. However, some applications depend on first-in, first-out (FIFO) ordering and may perform poorly or not at all on current versions of Windows (for example, applications that have been using critical sections as a rate-limiter). To ensure that your code continues to work correctly, you may need to add an additional level of synchronization. For example, suppose you have a producer thread and a consumer thread that are using a critical section object to synchronize their work. Create two event objects, one for each thread to use to signal that it is ready for the other thread to proceed. The consumer thread will wait for the producer to signal its event before entering the critical section, and the producer thread will wait for the consumer thread to signal its event before entering the critical section. After each thread leaves the critical section, it signals its event to release the other thread.

因此 post 中的算法是 Windows XP 及更早版本中临界区的简化版本。

上述算法不是一个完整的临界区,它缺乏递归支持、旋转、低资源情况处理。

也依赖于Windows事件的公平性。