操作系统 线程并行代码实现
个人理解,极其主观,仅供参考!
关于并发和并行,可以参考文章:多线程是同时执行多个线程的吗。个人理解并发是可以包含并行的概念的。
并发:
多个线程按照一定的顺序逐个占用CPU,执行任务,顺序由操作系统调度。
对于单核处理器而言,其多线程都是并发的。
对于多核处理器而言,其用户级线程是并发的。
并行:
多个线程同时占用CPU多个核心,执行任务(线程数要小于CPU的超线程数)。
对于单核处理器而言,其不存在并行现象。
对于多核处理器而言,其内核心线程是并行的。
**注意:**线程间不争夺系统资源才能产生并行。
代码实现:
环境:win10下使用mingw编译器(在Linux下使用pthread库创建的线程默认是内核级线程——Linux下调用pthread库创建的线程是属于用户级线程还是内核级线程?——其实在windows下也是如此)
int num = 1;
void do1()
{
printf("p1 do1\n");
}
void do2()
{
printf("p2 do2\n");
}
void* funP1(void* arg)
{
while(1)
{
do1();
while(num);
do2();
}
}
void* funP2(void* arg)
{
while(1)
{
sleep(10);
if(num)
{
num = 0;
}
else
{
num = 1;
}
}
}
int main()
{
pid_t p1,p2;
pthread_create(&p1,NULL,funP1,NULL);
pthread_create(&p2,NULL,funP2,NULL);
pthread_join(p1,NULL);
pthread_join(p2,NULL);
return 0;
}
程序输出:
程序运行结果如图,这明显与我们的预期不符,如果两个线程同时运行,经过了十秒后,num赋值为0,线程p1不再堵塞在while,do2被调用,应该会看到do2函数运行结果才对。
这里由于采用release版本编译,在线程p1里,由于只有一个读num值的语句,所以编译器进行了优化,每次读寄存器中的值以加快读取速度,其他线程更改了内存中的值,但寄存器的值没有改变,导致卡死在这里。
这时,就要加volatile关键字了,它告诉编译器这是个易变的变量,每次读取这个变量都要从内存中读数据。
加了volatile关键字后,程序的运行结果:
在十秒后,do1 do2被循环调用,再过十秒程序不再打印,循环往复。
印证了,在线程p1执行while循环的时候,p2的sleep函数也在运行,两者并行。
刚接触并发编程的时候,会写一个打印机模型,让两个线程分别打印不同的话:
void* funP1(void* arg)
{
while(1)
{
printf("THREAD1 IS RUNNING\n");
}
}
void* funP2(void* arg)
{
while(1)
{
printf("THREAD2 IS RUNNING\n");
}
}
如果两个线程是并行的,那么程序运行结果会是线程一线程二循环逐次打印,而实际上,程序的输出一段时间是线程一循环打印,一段时间是线程二循环打印,这显然不是并行的,而是并发的。
造成这种现象主要原因是,两个线程都需要占用进程下的同一资源——文件描述符stdout。
其实也不难理解,当两个线程同时更改了同一进程资源,那这个进程资源最后要依据哪个线程而改动呢。处理器可以有多个,但这块内存地址只有一个,当多个处理器通过总线去访问同一块数据时,会造成冲突,OS显然要避免这种事情的发生。