c++原子操作和加锁操作对多线程编程中临界资源的保护

文章目录

    • 首先看一段没有保护的多线程对共享变量进行操作的代码
    • 原子操作的效果
    • 加锁操作

首先看一段没有保护的多线程对共享变量进行操作的代码

#include
#include
#include
#include
#include

//准备创建16个线程
#define PTHREAD_NUM 16

//一个全局变量
unsigned long sum = 0;

void *thread(void *arg)
{
	for(int i = 0;i < 10000; i++)
	{	
		sum += 1;
	}
}

int main(void)
{
	printf("before,sum = %ld\n",sum);
	
	pthread_t pthread[PTHREAD_NUM];//pthread是指向进程标识符的指针
	int ret;//接收返回值
	void *retval[PTHREAD_NUM];

	for(int i = 0 ;i < PTHREAD_NUM; ++i)
	{
		ret = pthread_create(&pthread[i],NULL,thread,NULL);
		if(ret != 0)
		{
			perror("cause:");
			printf("创建第%d个线程失败\n",i+1);
		}
	}

	for(int i = 0; i < PTHREAD_NUM; ++i)
	{
		pthread_join(pthread[i],&retval[i]);
	}
	
	printf("after,sum = %ld\n",sum);
	
	printf("Goodbye!\n");
	return 0;
}

①额外注释

pthread_create()第一个参数为指向线程标识符的指针。
			第二个参数用来设置线程属性。
			第三个参数是线程运行函数的起始地址。
			最后一个参数是运行函数的参数。

	pthread_join()函数,以阻塞的方式等待thread指定的线程结束。
	当函数返回时,被等待线程的资源被收回。如果线程已经结束,那么该函数会立即返回。
	并且thread指定的线程必须是joinable的。

②解析:这段代码没有利用原子操作或加锁操作,所以跑起来的结果,sum的值不一定是预期的160000,如图:c++原子操作和加锁操作对多线程编程中临界资源的保护_第1张图片

原子操作的效果

还是先看代码

#include
#include
#include
#include
#include

//准备创建16个线程
#define PTHREAD_NUM 16

//一个全局变量
unsigned long sum = 0;

void *thread(void *arg)
{
	for(int i = 0;i < 10000; i++)
	{	
		//这一行是原子操作
		 __sync_fetch_and_add (&sum,1);//这行代码的注释单独放在下文
	}
}

int main(void)
{
	printf("before,sum = %ld\n",sum);
	
	pthread_t pthread[PTHREAD_NUM];//pthread是指向进程标识符的指针
	int ret;//接收返回值
	void *retval[PTHREAD_NUM];

	for(int i = 0 ;i < PTHREAD_NUM; ++i)
	{
		ret = pthread_create(&pthread[i],NULL,thread,NULL);
		if(ret != 0)
		{
			perror("cause:");
			printf("创建第%d个线程失败\n",i+1);
		}
	}

	for(int i = 0; i < PTHREAD_NUM; ++i)
	{
		pthread_join(pthread[i],&retval[i]);
	}
	
	printf("after,sum = %ld\n",sum);
	
	printf("Goodbye!\n");
	return 0;
}

__sync_fetch_and_add (&sum,1);这行代码的解析
这个系列的函数
type __sync_fetch_and_add (type *ptr, type value);
type __sync_fetch_and_sub (type *ptr, type value);
type __sync_fetch_and_or (type *ptr, type value);
type __sync_fetch_and_and (type *ptr, type value);
type __sync_fetch_and_xor (type *ptr, type value);
type __sync_fetch_and_nand (type *ptr, type value);
type __sync_add_and_fetch (type *ptr, type value);
type __sync_sub_and_fetch (type *ptr, type value);
type __sync_or_and_fetch (type *ptr, type value);
type __sync_and_and_fetch (type *ptr, type value);
type __sync_xor_and_fetch (type *ptr, type value);
type __sync_nand_and_fetch (type *ptr, type value);
参考资料: linux无锁化编程–__sync_fetch_and_add系列原子操作函数

用法是这样的,这个系列的函数看函数名我们就能猜出函数的功能。
比如说
type __sync_fetch_and_add (type *ptr, type value);
这个函数接受一个type 类型的指针ptr和type类型的数值value,执行对ptr执行的对象进行加value的操作。
因此,我们这行代码就是对全局变量sum进行效果是“sum+=1”的原子操作

__sync_fetch_and_add (&sum,1);

②ok,运行结果如我们所预期的,sum最后值都是160000
注意,g++编译命令要加-std=c++11这个参数
c++原子操作和加锁操作对多线程编程中临界资源的保护_第2张图片

加锁操作

老规矩,看代码,跟上面大同小异

#include
#include
#include
#include
#include

//定义一个互斥量mymutex
pthread_mutex_t mymutex=PTHREAD_MUTEX_INITIALIZER;

//准备创建16个线程
#define PTHREAD_NUM 16

//一个全局变量
unsigned long sum = 0;

void *thread(void *arg)
{
	for(int i = 0;i < 10000; i++)
	{	
		//上锁
		pthread_mutex_lock(&mymutex);
		sum += 1;
		//解锁
		pthread_mutex_unlock(&mymutex);
	}
}

int main(void)
{
	printf("before,sum = %ld\n",sum);
	
	pthread_t pthread[PTHREAD_NUM];//pthread是指向进程标识符的指针
	int ret;//接收返回值
	void *retval[PTHREAD_NUM];

	for(int i = 0 ;i < PTHREAD_NUM; ++i)
	{
		ret = pthread_create(&pthread[i],NULL,thread,NULL);
		if(ret != 0)
		{
			perror("cause:");
			printf("创建第%d个线程失败\n",i+1);
		}
	}

	for(int i = 0; i < PTHREAD_NUM; ++i)
	{
		pthread_join(pthread[i],&retval[i]);
	}
	
	printf("after,sum = %ld\n",sum);
	
	printf("Goodbye!\n");
	return 0;
}

聪明的你肯定也才到了,运行结果最后sum都是160000
c++原子操作和加锁操作对多线程编程中临界资源的保护_第3张图片

你可能感兴趣的:(c++基础)