Windows 线程:啥时候应该使用 InterlockedExchangeAdd()?
Posted
技术标签:
【中文标题】Windows 线程:啥时候应该使用 InterlockedExchangeAdd()?【英文标题】:Windows Threads: when should you use InterlockedExchangeAdd()?Windows 线程:什么时候应该使用 InterlockedExchangeAdd()? 【发布时间】:2010-01-14 21:55:10 【问题描述】:这个函数的命名似乎是一些复杂的事情。什么时候知道这是要走的路,而不是做这样的事情:
准备工作 CRITICAL_SECTION cs; int *p = malloc(sizeof(int)); // 分配站点 InitializeCriticalSection(&cs); // 第一次写入的提示
线程 #1 *p = 1; // 先写
线程 #2 EnterCriticalSection(&cs); *p = 2; // 第二次写入 LeaveCriticalSection(&cs);
我有一个写在一个线程中完成:
Run()
// some code
m_bIsTerminated = TRUE;
// some more code
然后,我有一个在另一个线程中完成的读取(可能同时):
Terminate()
// some code
if( m_bIsTerminated )
m_dwThreadId = 0;
m_hThread = NULL;
m_evExit.SetEvent();
return;
// even more code
解决这种竞争条件的最佳解决方案是什么?关键部分是要走的路还是使用 InterlockedExchangeAdd() 更有用?
【问题讨论】:
【参考方案1】:在您的情况下,没有竞争条件。变量永远不会重置为 FALSE,是吗?这只是线程的“请死”开关,对吗?那么就不需要任何形式的同步了。
InterlockedXXX 系列函数利用 Intel CPU 的原子 3 操作数命令(XADD 和 CMPXCNG)。所以它们比临界区便宜得多。而你想要的线程安全分配是 InterlockedCompareExchange()。
UPD: 并将变量标记为 volatile。
【讨论】:
有问题。该值可能位于一个内核的缓存中,而另一个内核可能需要相当长的时间才能看到更新后的值。 更新变量后使用内存屏障而不是易失性。这具有您真正想要的语义(执行所有写入now)。 Volatile 并不理想,因为 1) 它不会阻止写入被重新排序,并且 2) 它强制 all 写入变量立即写入,而您不一定需要。 【参考方案2】:InterlockedExchangeAdd 用于将值添加到整数作为原子操作,这意味着您不必使用临界区。如果您的一个线程抛出异常,这也消除了死锁的风险 - 您需要确保不保留任何类型的锁,因为这会阻止其他线程获取该锁。
对于您的场景,您绝对可以使用 Interlocked...- 函数,但我会使用事件(CreateEvent、SetEvent、WaitForSingleObject),可能是因为我经常发现自己需要等待多个对象(您可以等待在您的场景中为零秒)。
更新:对变量使用 volatile 可能有效,但不建议这样做,例如:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2016.html 和 http://www-949.ibm.com/software/rational/cafe/blogs/ccpp-parallel-multicore/tags/c%2B%2B0x。
如果您想便携,请查看boost::thread。
【讨论】:
我将事件用于正常的线程开始和结束事件,但这是用于当您想在线程完成工作之前立即终止时。我想确保它优雅地终止并将线程放回池中,以便以后可以在必要时使用它。 这些调用是特定于 Windows 的。 @Omnifarious:由于 OP 提到了 InterlockedExchangeAdd 并且问题被标记为 Windows,我认为可以公平地假设问题也是 Windows 特定的。 @Brian:我不确定我是否跟随。您是否正在寻找比正常退出更快的退出方式?除非我误解了您的问题,否则没有什么可以阻止您为此使用事件。 我猜这是 Windows 特定的,现在 b/c Neil Butterworth 决定编辑我的原始帖子。最初的问题没有提到任何关于 Windows 的内容。将来,如果可能的话,我们希望我们的代码也可以移植到 Linux 和 MAC 系统上。但目前我们仍坚持使用 Windows,并尝试尽可能少地做一些特定于 Windows 的事情。【参考方案3】:确保 m_bIsTerminated 被标记为 volatile,你应该没问题。尽管在我看来,在将“终止”设置为 true 之后,您会 // 再添加一些代码。该变量究竟表示什么?
您的“竞争条件”是您的各种元素 // 更多代码可以以不同的顺序执行。你的变量没有帮助。您的目标是让它们以确定的顺序执行吗?如果是,您需要一个条件变量来等待一个线程并设置另一个线程。如果您只是不希望它们同时执行,那么临界区就可以了。
【讨论】:
表示当前线程已收到终止信号,然后将线程返回到池中,而其他线程继续运行,除非它们也收到终止信号。 volatile 在这个例子中就足够了,一个线程在读,一个线程在写。 你是对的 - 另一个线程看到写入的确切顺序并不重要,只是它确实看到它。以上是关于Windows 线程:啥时候应该使用 InterlockedExchangeAdd()?的主要内容,如果未能解决你的问题,请参考以下文章
在 Windows 上,啥时候应该使用“\\\\?\\”文件名前缀?
我们啥时候应该在 IIS 中托管 WCF 服务,啥时候应该在 Windows 服务中托管?
我应该啥时候使用 ConcurrentSkipListMap?