我的应用程序中有这段代码。我怀疑它不是线程安全的,所以决定问问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可以在一天内工作数百万次。在访问int_ptr
的99.99999%的尝试中,第二个线程将进入和退出临界区。这对我来说没有意义。该应用程序仅适用于Windows 2000及更高版本,其中英特尔处理器显然具有多核。
这段代码是否是线程安全的?如果没有,我该怎么做才能使它具有线程安全性?我怎么能原子地读取int_ptr?一世。即:
int * dummy_ptr = read_atomic<int *>(int_ptr);
return dummy_ptr[index];
包括std::vector
在内的解决方案让我更开心,更舒服。
答案 0 :(得分:7)
不,这不安全。 get_value
可以读取int_ptr
的值,然后计划出来。然后另一个线程可以换出旧值int_ptr
和delete
。重新插入get_value
后,它会尝试取消引用从int_ptr
读取的值 - 该值已被删除。
更好的方法是基于"Read-copy-update" (RCU),它已经在Linux中发挥了很大作用。基本原则是你不要立即删除旧的值 - 你等到某个时候你可以保守地确定没有任何东西仍然有旧的指针值,然后删除它。 / p>
不幸的是,Windows上还没有RCU库实现。我想你可以尝试将urcu移植到Windows上。
答案 1 :(得分:2)
不,不是。一种情况是thread2在get_value
函数内部并且正在执行int_ptr[index]
。由于它不是原子的,因此可能需要几个指令。在这些指令的中间,线程上下文切换发生,thread1开始用grow
执行。这将delete[]
int_ptr
。现在,当thread2启动时,它将遇到访问冲突。您可以使用CCriticalSection
来解决此问题。由于它不是内核对象,因此在Windows操作系统上性能并不差。
答案 2 :(得分:0)
考虑让线程1要求线程2进行更新或等待执行更新。
为了使它有用,线程2必须从系统的其余部分侦听某种信号或消息。可以使用新消息扩展消息传递机制或条件变量。还要考虑APC(有点像Unix信号)。
这需要实际上是要求而不是强制暂停。强行暂停线程无法解决问题,因为线程可能在任何时候被暂停,包括在阅读int_ptr
和阅读int_ptr[index]
之间。