linux多线程并发教程交流(linuxc编程之多线程)
linux多线程并发教程交流(linuxc编程之多线程)三步以后,g_count的值被顺利加1。3.把寄存器eax中的内容移动到内存g_count的地址g_count ;被分为三步操作:1.把g_count的内容从内存中移动到寄存器eax2.把寄存器eax加1
在多线程中操作全局变量一般都会引起线程冲突,为了解决线程冲突,引入原子操作。
1.线程冲突#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#include <Windows.h>
int g_count = 0;
void count(void *p)
{
Sleep(100); //do some work
//每个线程把g_count加1共10次
for (int i = 0; i < 10; i )
{
g_count ;
}
Sleep(100); //do some work
}
int main(void)
{
printf("******多线程访问全局变量演示***by David***\n");
//共创建10个线程
HANDLE handles[10];
for (int i = 0; i < 10; i )
{
for (int j = 0; j < 10; j )
{
handles[j] = _beginthread(count 0 NULL);
}
WaitForMultipleObjects(10 handles 1 INFINITE);
printf("%d time g_count = %d\n" i g_count);
//重置
g_count = 0;
}
getchar();
return 0;
}
运行
理论上,g_count的最后结果应该是100,可事实却并非如此,不但结果不一定是100,而且每次的结果还很可能不一样。原因是,多个线程对同一个全局变量进行访问时,特别是在进行修改操作,会引起冲突。详细解释:
设置断点,查看反汇编
g_count ;被分为三步操作:
1.把g_count的内容从内存中移动到寄存器eax
2.把寄存器eax加1
3.把寄存器eax中的内容移动到内存g_count的地址
三步以后,g_count的值被顺利加1。
cpu执行的时间片内包含多条指令,每条指令执行期间不会被打断,但如果一个操作包含多条指令,则很有可能该操作会被打断。g_count ;就是这样的操作。
g_count被顺利加到100的前提:每次加1,都建立在上一次加1顺利完成的基础上。也就是说,如果上一次加1被打断,这一次的加1就得不到上一次加1的累积效果。自然,最后的结果,多半会小于100。
2.原子操作所谓原子操作,是指不会被线程调度机制打断的操作,操作一旦开始,就得执行到结束为止。原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序是不可以被打乱,或者切割掉只执行部分。原子操作一般靠底层汇编实现。
在头文件winnt.h中提供了很多的原子操作函数,它们使用自加锁的方式,保证操作的原子性,如自增操作
InterlockedIncrement,
函数原型
LONG CDECL_NON_WVMPURE InterlockedIncrement(
_Inout_ _Interlocked_operand_ LONG volatile *Addend
);
其它相关操作,请自行查看头文件。
使用该函数,我们修改线程函数count。修改很简单,只需把g_count 改为InterlockedIncrement((LONG)&g_count);即可。
运行如下
显然,在原子操作下,我们肯定是可以得到正确结果的。
需要C/C Linux服务器架构师学习资料私信“资料”(资料包括C/C ,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享