自写临界区
单行代码原子操作

多核指令的执行过程
一条指令的执行过程是: CPU先读取指令到CPU内部,然后才是执行该指令。而在多核多线程的情况下。可能出现多个CPU同时读到了同一条指令,这样线程就会出问题。 那么解决访问是,限制CPU读取指令,只能有一个CPU读取同一条指令,该操作成为原子操作。
LOCK 就可以对一个内存加锁,同一时刻只能有一个CPU来读取该内存的指令。
使用临界区,互斥体也可以解决多线程安全问题,但是效率太低。例如临界区,是一个线程进入临界区后把其他线程给挂起来,执行完后再恢复其他线程。这样会对CPU性能造成很大的影响。
而如果把代码改成对访问的公共内存加锁,这样就可以提高效率。还能保证多线程安全不会出问题。
for (int i=0;i<10000000;i++){__asm {lea eax, a //这里使用取地址,就是为了对内存加锁 而不能用mov eax,a 对寄存器加锁是没有意义的,因为多核情况下有多个eax寄存器.lock inc dword ptr ds : [eax] //这里限制了,在同一时刻只能有一个CPU执行这一行代码,去掉lock就会发生问题//inc dword ptr ds : [eax] 如果写成这样,单核是安全的 ,多核就会出现问题,可能有两个CPU同时把读取了a的值,读取的时候是一样的值,而执行完这条指令,结果只+1。}}
临界资源
全局变量,文件 这种不能让多个CPU同时来操作的资源就是临界资源。
多行代码原子操作
自己实现临界区
#include
#include DWORD dwFlag = 0;__declspec(naked) void ScritiSection()
{__asm {start: mov eax, 1lock xadd[dwFlag], eax //必须要使用这种读取值,和设置值在一条指令实现的汇编指令。cmp eax, 0jz endLablock dec[dwFlag] }Sleep(1);__asm {jmp startEndLab:ret }
}
void LeaveSecritiSection()
{__asm {lock dec[dwFlag]}}int a = 0;
void fun1(void* p)
{for (int i = 0; i < 1000011; i++){ScritiSection();a++; //临界区代码 访问临界资源LeaveSecritiSection();}
}void fun2(void* p)
{for (int i = 0; i < 1000000; i++){ScritiSection();a++;LeaveSecritiSection();}
}
int main(int argc, char * argv[])
{DWORD tid1;DWORD tid2;HANDLE hThread[2] = { 0 };hThread[0]=CreateThread(0, 0, (LPTHREAD_START_ROUTINE)fun1, 0, 0, &tid1);hThread[1] = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)fun2, 0, 0,&tid2);WaitForMultipleObjectsEx(2, hThread,TRUE,INFINITE,0);printf("a=%d\n", a);getchar();return 0;
}
总结:
临界区的本质就是线程切换(Sleep的本质就是主动切换线程),非常消耗资源。更加轻量级,效率更高的解决方案是使用自旋锁。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
