如何将关键部分传递给另一个线程?
Posted
技术标签:
【中文标题】如何将关键部分传递给另一个线程?【英文标题】:How to pass Critical Section to another thread? 【发布时间】:2020-03-29 19:29:15 【问题描述】:我有 3 个线程,同时恢复,用不同的参数调用同一个函数。 如何强制线程离开临界区并将其传递给另一个线程?
当我运行下面的代码时,while 循环被多次调用,直到另一个线程进入临界区(它也循环了很多次)。
DWORD WINAPI ClientThread(LPVOID lpParam)
// thread logic
while(true)
EnterCriticalSection(&critical);
// thread logic
LeaveCriticalSection(&critical);
Sleep(0);
// thread logic
return 0;
换句话说,我怎样才能防止线程立即重新进入一个部分?
【问题讨论】:
如果想让它顺序运行,为什么需要多个线程呢?只需在一个线程中按顺序运行您的代码。 仍然不确定为什么需要线程,但也许this other question 会有所帮助。 这听起来很像XY problem。 您可能想了解ticket locks。但是,我怀疑这是否是您所需要的,因为我仍然确信这是一个 XY 问题。 @Mike:线程不需要同步来访问只读共享资源。如果每个线程都有自己的可写资源副本,那么这些资源也不需要同步。只有共享的可写资源才需要同步。如果访问这些资源会导致如此多的线程争用,以至于锁的公平性成为一个主要问题,那么如果您只有一个线程,您的代码可能会运行得更快。 【参考方案1】:您不能直接要求线程离开临界区。线程执行完毕后会离开它。
所以唯一的方法是阻止它进入关键部分,或者“要求”它提前完成。例如。通过在该部分中连续检查 atomic_flag 并中止停止线程的操作(如果已检查)。
如果你想阻止一个线程在它离开后直接重新进入一个部分,你可以让它,这将重新安排线程的执行。 如果您想从线程(A->B->C->D->A->B ...)中获得准确的顺序,您需要编写一个自定义调度程序或一个自定义“fair_mutex”来检测其他等待线程。
编辑:
这样的函数是BOOL SwitchToThread();
doc
【讨论】:
是的,我想防止线程直接重新进入一个部分。我不在乎接下来会运行哪个线程。我尝试了 YieldProcessor() 和 SwitchToThread() 但它们什么也没做。 @Mike:如果这是您想要的,那么您将不得不实际编写该逻辑,正如 F*** 所说。不仅仅是一个简单的功能可以确保为您服务。此外,您几乎不应该遇到您想要的行为(尤其是在现代多核 CPU 上),因此您可能实施了错误的设计。【参考方案2】:正如另一个答案中提到的,您需要 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 中找到以下内容:
从带有 Service Pack 1 (SP1) 的 Windows Server 2003 开始,线程 等待临界区不获取临界区 先到先得。此更改提高了性能 对于大多数代码来说意义重大。但是,某些应用程序依赖于 先进先出 (FIFO) 排序,可能表现不佳或不 所有在当前版本的 Windows 上(例如,应用程序 一直使用临界区作为速率限制器)。为了保证 您的代码继续正常工作,您可能需要添加 额外的同步级别。例如,假设您有一个 使用关键线程的生产者线程和消费者线程 section 对象来同步他们的工作。创建两个事件对象, 每个线程使用一个来表示它已为另一个线程准备好 线程继续。消费者线程将等待生产者 在进入临界区之前发出它的事件信号,并且 生产者线程将等待消费者线程发出其事件信号 在进入临界区之前。每个线程离开后 临界区,它发出释放另一个线程的信号。
因此,本文中的算法是 Windows XP 及更早版本中关键部分的简化版本。
上述算法不是一个完整的临界区,它缺乏递归支持、旋转、低资源情况处理。
它还依赖于 Windows 事件公平性。
【讨论】:
以上是关于如何将关键部分传递给另一个线程?的主要内容,如果未能解决你的问题,请参考以下文章