c语言实现多线程并发

2021-06-28  本文已影响0人  梁帆

首先,c语言的多线程并发,需要用到 pthread.h 库。

#include <pthread.h>

1、开启一个线程

下面代码是最基本的多线程实现:

主要分为三步:

1、声明一个线程变量th,类型为pthread_t;

2、使用pthread_create函数进行创建,第一个参数是线程变量的地址,第三个参数是线程执行的函数(返回值为void*);

3、pthread_join函数等待;

编译的时候要注意,涉及到多线程的时候,得在gcc参数里加上 -lpthread:

可以发现,成功输出了hello world。

2、开启两个线程

当我们开启两个线程,代码如下:

执行的任务都是打印1~499的时候可以发现:

输出的时候,两个线程是错序的。

再对代码做以下修改:

这里我们用到了pthread_create函数的第4个参数,这个参数的传入会反应到myfunc中的形参中去。最后输出的结果,我们可以清晰地看出th1和th2的线程标记和交错运行:

这里th2输出数字58的时候,th1才开始输出数字1。

3、多线程进行协同运算

我们创建一个数组,其中有5000个元素,我们想用两个线程来共同计算这5000个元素的加法和。

从两个线程的函数可以看出,一个线程计算前2500个值的加法和,另一个线程计算后2500个值的加法和。

main函数中,在pthread_join函数等待的th1和th2都结束后,输出对应的值。

编译运行后:

代码改进:

可以看到我们的代码里,th1和th2的执行函数中有大量的相似代码,所以我们最后用一个函数来复用,不难想到,需要通过传参的方式来实现代码复用。这里我们定义了一个结构体,结构体中有循环的起始标记first,终止标记last,区间内加法和result。

从上图可以看出,我们把myfunc1和myfun2整合到了myfunc中去。而在main函数中,我们创建线程的时候传入的参数正是结构体指针:

这样在myfunc函数中,我们就可以对传入的结构体参数中的元素进行利用了,将计算所得传到结构体的result中去。这样我们输出加法和,就可以得到跟上面一样的结果,但是代码会更整洁漂亮:

4、并发程序引起的共享内存问题

有如下代码。有两个进程,两个进程共享全局变量s。两个进程都执行一个计数功能的函数,直观地看过去,th1运行时s++要执行10000次,th2运行时s++也要执行10000次,似乎计算得到的最后s应该是20000。但实际上是这样的吗?

编译运行后,发现输出如下:

s并不是20000,而是12657。

原因其实很简单:

当我们执行s++,底层发生的事件其实是:内存中读取s→将s+1→将s写入到内存。这不是一个原子化操作,当两个线程交错运行的时候,很容易发生结果的丢失。因此最后的结果肯定是要小于20000的。这种情况有种专有名词,叫race condition。

为了解决这个问题,我们可以加锁

改进后的代码如下,学过操作系统会很好理解,无非就是为了保证共享内存区(临界区)的原子化操作,我们可以在进这段代码之前加锁(pthread_mutex_lock),意味着其他线程看到这段内存被其他人占有的时候,就不去抢占,等这段内存被解锁(pthread_mutex_unlock)之后,它才有读写这段临界区的权利。

但其实这种方式的执行速度并不快,比如这段代码里,每个线程都要进行10000次加解锁的操作,它能解决内存读写冲突的问题,但是却牺牲了效率。

上一篇下一篇

猜你喜欢

热点阅读