【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
关于内存屏障,我曾经写过一篇文章,也就是这。今天,我自己又发现了关于一篇内存屏障的好文章,也就是这。在这篇文章中,我发现它设计的例子特别好,但是存在部分错误,而且也没有windows的版本,所以我打算移植一下。
晚上,利用跑步后的这一段时间把代码整理了一下,终于可以运行了。关于内存屏障,本质上其实就是多核cpu的cache同步。如果有不清楚的同学,可以参考这篇文章,即这, 题目为Memory Barriers: a Hardware View for Software Hackers。
闲话不多说,先给大家看一下代码。
/* * file: test memory barrier * compiler : vs2010 and above */ #include <windows.h> #include <stdio.h> #include <assert.h> typedef unsigned char u8; typedef signed char s8; typedef unsigned short u16; typedef signed short s16; typedef unsigned int u32; typedef signed int s32; BOOL state[2]; u8 turn; static u32 count = 0; u32 val = 1000000; //#define MEM_BARRIER() do {__asm {mfence}}while(0) #define MEM_BARRIER() DWORD WINAPI ThreadProc(LPVOID* args) { u8 flag = (u8) args; u32 num = 0; while(num++ < val) { state[flag] = TRUE; turn = 1 - flag; MEM_BARRIER(); while((turn == (1 - flag)) && state[1 - flag]); count++; state[flag] = FALSE; } return 0; } int main(int argc, char* argv[]) { HANDLE hThread[2]; while(1) { count = 0; turn = 0; state[1] = state[0] = FALSE; hThread[0] = CreateThread(NULL, 0, ThreadProc, 0, CREATE_SUSPENDED, NULL); SetThreadAffinityMask( hThread[0], 0x01 ); hThread[1] = CreateThread(NULL, 0, ThreadProc, 1, CREATE_SUSPENDED, NULL); SetThreadAffinityMask( hThread[1], 0x02 ); ResumeThread(hThread[0]); ResumeThread(hThread[1]); WaitForMultipleObjects(2, hThread, TRUE, INFINITE); if(count != 2000000) { __asm {int 3} } } return 1; }
整个代码最精彩的地方就是利用Peterson算法实现thread之间的互斥。本来对于单core的thread来说,这是没有问题的。但是关键在于当前一个thread运行在core0,另外一个运行在core1之上。这就很可能造成共享变量count是不同步的(cache级别的不同步)。如果大家不使用mfence指令的话,就会发现某些情况下count不是2000000,此时系统就会触发中断。同样,一旦大家使用了这个神奇的指令,之前出现的问题就不复存在了。memory barrier的范例本来就不多,这个算得上是目前为止我所知道的最棒的范例。
注: 此处的mfence也可以修改为cpuid,cpuid也有mfence的功能。所以如果修改成cpuid之后,就可以在vc6上运行了。
代码需要在vs2010版本之上运行。欢迎大家一试究竟。体会一下神奇的memory barrier。最后附上linux下的内存屏障代码,编译方法为g++ test.cpp -lpthread,输入./a.out即可。
#include <stdio.h> #include <pthread.h> #define mb() asm volatile ("mfence\n\t":::"memory") int counter = 0; class Peterson { public: Peterson() { turn = 0; state[0] = false; state[1] = false; } void lock(int threadID) { int self = threadID; int oppo = 1 - self; state[self] = true; turn = oppo; // mb(); while (state[oppo] && turn == oppo) ; } void unlock(int threadID) { int me = threadID; state[me] = false; } private: bool state[2]; int turn; }; Peterson mtx; void* thread(void *arg) { int threadID = (int)arg; int i = 0; while (i++ < 1000000) { mtx.lock(threadID); ++counter; mtx.unlock(threadID); } } int main() { pthread_t t1, t2; pthread_create(&t1, NULL, thread, (void*)0); pthread_create(&t2, NULL, thread, (void*)1); pthread_join(t1, NULL); pthread_join(t2, NULL); fprintf(stderr, "counter: %d\n", counter); return 0; }