【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱: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;
}