Windows操作系统中的线程安全和原子读取

时间:2011-02-04 12:46:27

标签: c++ windows multithreading thread-safety atomic

我的应用程序中有这段代码。我怀疑它不是线程安全的,所以决定问问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在内的解决方案让我更开心,更舒服。

3 个答案:

答案 0 :(得分:7)

不,这不安全。 get_value可以读取int_ptr的值,然后计划出来。然后另一个线程可以换出旧值int_ptrdelete。重新插入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]之间。