数据竞争

Version >= C11

C11 引入了对多线程执行的支持,这提供了数据竞争的可能性。程序包含数据竞争,如果其中的对象由两个不同的线程访问 1 ,其中至少一个访问是非原子的,至少一个修改该对象,并且程序语义无法确保两个访问不能重叠时间。 2 请注意,所涉及的访问的实际并发性不是数据竞争的条件; 数据竞争涵盖了由不同线程的内存视图中的(允许)不一致引起的更广泛的问题。

考虑这个例子:

#include <threads.h>

int a = 0;

int Function( void* ignore )
{
    a = 1;

    return 0;
}

int main( void )
{
    thrd_t id;
    thrd_create( &id , Function , NULL );

    int b = a;

    thrd_join( id , NULL );
}

主线程调用 thrd_create 启动一个新的线程运行函数 Function。第二个线程修改 a,主线程读取 a。这些访问都不是原子的,并且两个线程无论是单独还是联合都不做任何事情以确保它们不重叠,因此存在数据竞争。

该计划可以避免数据竞争的方式之一

  • 主线程可以在启动另一个线程之前执行 a 的读取;
  • 主线程在通过 thrd_join 确认对方已终止后,可以执行对 a 的读取;
  • 线程可以通过互斥锁同步它们的访问,每个互斥锁在访问 a 之前锁定该互斥锁并在之后解锁它。

正如互斥锁选项所示,避免数据争用不需要确保特定的操作顺序,例如在主线程读取之前修改 a 的子线程; 足以(避免数据竞争)确保对于给定的执行,一个访问将在另一个之前发生。

1 修改或读取对象。

2 (引自 ISO:IEC 9889:201x,第 5.1.2.4 节“多线程执行和
数据竞争”)如果程序的执行在不同的线程中包含两个冲突的动作,其中至少有一个是不是原子的,也不会发生在另一个之前。任何此类数据争用都会导致未定义的行为。