Windows 操作系统中的线程安全和原子读取
Posted
技术标签:
【中文标题】Windows 操作系统中的线程安全和原子读取【英文标题】:Thread-safety and atomic reading in Windows OS 【发布时间】:2011-02-04 12:46:27 【问题描述】:我的应用程序中有这段代码。我怀疑它不是线程安全的,所以决定问问 SOers。
int * volatile int_ptr;
int count;
线程 1:
void grow(int new_count)
if(new_count <= count) return;
int * new_ptr = new int[new_count];
memset(new_ptr, 0 , new_count * sizeof(int));
memcpy(new_ptr,int_ptr,count);
int * dum_ptr = (int *)InterlockedExchange((volatile long *)&int_ptr,(long)new_ptr)
count = new_count;
delete [] dum_ptr;
线程 2:
int get_value(int index)
return int_ptr[index];
我知道可以使用 CRITICAL_SECTION,但线程 1 可能一周工作一次,而线程 2 一天工作数百万次。在 99.99999% 的访问 int_ptr
的尝试中,第二个线程将无缘无故地进入和退出临界区。这对我来说没有意义。该应用程序只能在 Windows 2000 和更高版本中运行,并且显然具有多核英特尔处理器。
这段代码是线程安全的吗?如果不是,我应该怎么做才能使它成为线程安全的?如何以原子方式读取 int_ptr ?一世。 e. :
int * dummy_ptr = read_atomic<int *>(int_ptr);
return dummy_ptr[index];
一个解决方案,包括std::vector
,让我更快乐、更舒服。
【问题讨论】:
volatile
在这里没有为你做任何事情
@John Dibling:严格来说是为了我的实现还是一般?
不是要拖钓,但如果您认为可以摆脱此代码,您的用户名是合适的。 :-)
两者,真的。 volatile
在多线程编程中毫无用处。见:***.com/questions/4557979/…
【参考方案1】:
不,这不安全。 get_value
可以读取int_ptr
的值,然后被调度出去。然后另一个线程可以换出 int_ptr
和 delete
旧值。当get_value
被换回时,它会尝试取消引用它从int_ptr
读取的值 - 该值已被删除。
更好的方法是基于"Read-copy-update" (RCU),它已经在Linux 中得到了很好的应用。基本原则是您不要立即删除旧值 - 您等到某个时间点可以保守地确定没有任何东西仍然具有旧指针值,然后将其删除。
很遗憾,Windows 上还没有 RCU 库实现。我想你可以尝试将urcu 移植到 Windows。
【讨论】:
【参考方案2】:不,不是。一种情况是 thread2 在 get_value
函数内,并且正在执行 int_ptr[index]
。由于它不是原子的,这可能需要几个指令。这些指令执行到一半时,发生线程上下文切换,thread1 开始以grow
执行。这将delete[]
int_ptr
。现在当 thread2 启动时,它会遇到访问冲突。您可以使用CCriticalSection
来解决这个问题。由于它不是内核对象,因此在 Windows 操作系统上性能还不错。
【讨论】:
CCriticalSection 有一个内置的 CRITICAL_SECTION 实现。你认为我没有 CRITICAL_SECTIONs 的替代品【参考方案3】:考虑让线程 1 要求线程 2 执行更新或在执行更新时等待。
为了使这个有用,线程 2 必须侦听来自系统其余部分的某种信号或消息。可以使用新消息扩展消息传递机制或条件变量。还要考虑 APC(有点像 Unix 信号)。
这需要实际询问而不是强行暂停。强行挂起线程并不能解决问题,因为线程可能在任何时候挂起,包括在读取int_ptr
和读取int_ptr[index]
之间。
【讨论】:
以上是关于Windows 操作系统中的线程安全和原子读取的主要内容,如果未能解决你的问题,请参考以下文章