理解 Memory barrier(内存屏障)【转】 (4)

assert(pthread_create(&t1, NULL, thread1, NULL) == 0);

assert(pthread_create(&t2, NULL, thread2, NULL) == 0);

 

cpu_set_t cs;

CPU_ZERO(&cs);

CPU_SET(cpu_thread1, &cs);

assert(pthread_setaffinity_np(t1, sizeof(cs), &cs) == 0);

CPU_ZERO(&cs);

CPU_SET(cpu_thread2, &cs);

assert(pthread_setaffinity_np(t2, sizeof(cs), &cs) == 0);

 

while (1) {

start();

pthread_barrier_wait(&barrier_start);

pthread_barrier_wait(&barrier_end);

end();

}

 

return 0;

}

这里创建了两个线程来运行测试代码(需要测试的代码将放置在 run 函数中)。我使用了 pthread barrier(区别于本文讨论的 Memory barrier)主要为了让两个子线程能够同时运行它们的 run 函数。此段代码不停的尝试同时运行两个线程的 run 函数,以便得出我们期望的结果。在每次运行 run 函数前会调用一次 start 函数(进行数据初始化),run 运行后会调用一次 end 函数(进行结果检查)。run1 和 run2 两个函数运行在哪个 CPU 上则通过 cpu_thread1 和 cpu_thread2 两个变量控制。
先编译此程序:g++ -lpthread -o test2 test2.cpp(这里未优化,目的是为了避免编译器优化的干扰)。需要注意的是,两个线程运行在两个不同的 CPU 上(CPU 0 和 CPU 1)。只要内存不出现乱序访问,那么 r1 和 r2 不可能同时为 0,因此断言失败表示存在内存乱序访问。编译之后运行此程序,会发现存在一定概率导致断言失败。为了进一步说明问题,我们把 cpu_thread2 的值改为 0,换而言之就是让两个线程跑在同一个 CPU 下,再运行程序发现断言不再失败。

最后,我们使用 CPU Memory barrier 来解决内存乱序访问的问题(X86-64 架构下):

int cpu_thread1 = 0;

int cpu_thread2 = 1;

 

void run1()

{

x = 1;

__asm__ __volatile__("mfence" ::: "memory");

r1 = y;

}

 

void run2()

{

y = 1;

__asm__ __volatile__("mfence" ::: "memory");

r2 = x;

}

准备使用 Memory barrier

Memory barrier 常用场合包括:

实现同步原语(synchronization primitives)

实现无锁数据结构(lock-free data structures)

驱动程序

实际的应用程序开发中,开发者可能完全不知道 Memory barrier 就可以开发正确的多线程程序,这主要是因为各种同步机制中已经隐含了 Memory barrier(但和实际的 Memory barrier 有细微差别),这就使得不直接使用 Memory barrier 不会存在任何问题。但是如果你希望编写诸如无锁数据结构,那么 Memory barrier 还是很有用的。

通常来说,在单个 CPU 上,存在依赖的内存访问有序:

Q = P;

D = *Q;

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzsdzs.html