C语言之简单使用互斥锁实现并发控制操作

Posted 你是小KS

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言之简单使用互斥锁实现并发控制操作相关的知识,希望对你有一定的参考价值。

当前版本:eclipse c++MinGW-W64-builds-4.3.5widnwos

1. 声明

当前内容主要为测试使用互斥锁实现并发控制效果,分析C语言中线程并发

2. 不加锁的并发代码

#include <stdio.h>
#include <stdlib.h>
// 从unistd中导入sleep函数
#include<unistd.h>
// 导入需要使用的线程库
#include <pthread.h>
/**
 * 	@description 当前内容主要为测试多线程访问同一个变量的操作,测试并发问题
 * 	@author hy
 * 	@createTime 2022-04-16
 */

void startThread(void *(*run)(void *), void *arg);
int shutdownNum = 3;
static int modifyNum = 0; // 一个全局的共享变量
int defaultSleepTime = 50;
int executeNum = 5;
// 定义互斥锁标记
//pthread_mutex_t lock;

void lockAndIncreament() 
	//pthread_mutex_lock(&lock);
	modifyNum += 1;
	//pthread_mutex_unlock(&lock);


// 这里表示一个线程
void* run1(void* arg) 
	char* thread_name = NULL;
	// 校验参数数量和参数个数
	if (!arg || arg <= 0) 
		printf("exit with error!\\n");
		shutdownNum--;
		// 将参数的值进行赋值操作
		pthread_exit("arg len <= 0");
	 else 
		thread_name = ((char**) arg)[0];
	
	int num = 0;
	// 执行特定次打印就结束
	while (num < executeNum) 
		//lockAndIncreament();
		modifyNum+=1;
		++num;
		//pthread_mutex_lock(&lock);
		printf("print %s : modifyNum : %d\\n", thread_name, modifyNum);
		//pthread_mutex_unlock(&lock);
		fflush(stdout);
		// 表示使用打印为1秒每次sleep(1);
		sleep(1); //表示休眠200毫秒
	
	printf("%s exit!\\n", thread_name);
	fflush(stdout);
	shutdownNum--;
	pthread_exit("num >5");
	return NULL;


void* run2(void* arg) 
	char* thread_name = NULL;
	// 校验参数数量和参数个数
	if (!arg || arg <= 0) 
		printf("exit with error!\\n");
		shutdownNum--;
		// 将参数的值进行赋值操作
		pthread_exit("arg len <= 0");
	 else 
		thread_name = ((char**) arg)[0];
	
	int num = 0;
	// 执行特定次打印就结束
	while (num < executeNum) 
		//lockAndIncreament();
		modifyNum+=1;
		++num;
		//pthread_mutex_lock(&lock);
		printf("print %s : modifyNum : %d\\n", thread_name, modifyNum);
		//pthread_mutex_unlock(&lock);
		fflush(stdout);
		// 表示使用打印为1秒每次sleep(1);
		sleep(1); //表示休眠200毫秒
	
	printf("%s exit!\\n", thread_name);
	fflush(stdout);
	shutdownNum--;
	pthread_exit("num >5");
	return NULL;

void* run3(void* arg) 
	// 得到当前执行的线程的执行id
	char* thread_name = NULL;
	// 校验参数数量和参数个数
	if (!arg || arg <= 0) 
		printf("exit with error!\\n");
		shutdownNum--;
		// 将参数的值进行赋值操作
		pthread_exit("arg len <= 0");
	 else 
		thread_name = ((char**) arg)[0];
	
	int num = 0;
	// 执行特定次打印就结束
	while (num < executeNum) 
		//lockAndIncreament();
		modifyNum+=1;
		++num;
		//pthread_mutex_lock(&lock);
		printf("print %s : modifyNum : %d\\n", thread_name, modifyNum);
		//pthread_mutex_unlock(&lock);
		fflush(stdout);
		// 表示使用打印为1秒每次sleep(1);
		sleep(1); //表示休眠200毫秒
	
	printf("%s exit!\\n", thread_name);
	fflush(stdout);
	shutdownNum--;
	pthread_exit("num >5");
	return NULL;


void* run4(void* arg) 

	char* thread_name = NULL;
	// 校验参数数量和参数个数
	if (!arg || arg <= 0) 
		printf("exit with error!\\n");
		shutdownNum--;
		// 将参数的值进行赋值操作
		pthread_exit("arg len <= 0");
	 else 
		thread_name = ((char**) arg)[0];
	
	int num = 0;
	while (num < executeNum) 
		//lockAndIncreament();
		modifyNum+=1;
		++num;
		//pthread_mutex_lock(&lock);
		printf("print %s : modifyNum : %d\\n", thread_name, modifyNum);
		//pthread_mutex_unlock(&lock);
		fflush(stdout);
		// 表示使用打印为1秒每次sleep(1);
		sleep(1); //表示休眠200毫秒
	
	printf("%s exit!\\n", thread_name);
	fflush(stdout);
	shutdownNum--;
	pthread_exit("num >5");
	return NULL;


int main(int argc, char **argv) 
	void *(*func1)(void *) = run1;
	void *(*func2)(void *) = run2;
	void *(*func3)(void *) = run3;
	void *(*func4)(void *) = run4;
	char* arg1[2] =  "thread-1" ;
	void* args1 = arg1; // 使用携带正确参数的可以执行,否则认为不可执行
	printf("before create thread\\n");
	char* arg2[2] =  "thread-2" ;
	void* args2 = arg2;
	char* arg3[2] =  "thread-3" ;
	void* args3 = arg3;
	char* arg4[2] =  "thread-4" ;
	void* args4 = arg4;
	// 启动三个线程,执行不同的方法(如果执行的方法相同则不会出现并发?)
	// 解决多线程问题
	// 1. 初始化互斥锁
	//pthread_mutex_init(&lock, NULL);
	startThread(func1, args1);
	startThread(func2, args2);
	startThread(func3, args3);
	startThread(func4, args4);

	//startThread(func1, args1);
	// startThread(func1, args2);
	// startThread(func1, args3);
	printf("after create thread\\n");
	while (shutdownNum > 0) 
		//printf("main thread wait! shutdownNum = %d\\n",shutdownNum);
		//fflush(stdout);
		sleep(1);
	
	printf("main thread shutdown!\\n");
	printf("modifyNum = %d", modifyNum);
	// 销毁互斥锁
	//pthread_mutex_destroy(&lock);
	return 0;

// 表现为启动一个线程,给定一个函数和参数
void startThread(void *(*run)(void *), void *arg) 
	pthread_t th;
	const pthread_attr_t *attr = NULL;
	void *(*func)(void *) = run;
	int result = pthread_create(&th, attr, func, arg);
	if (result != 0) 
		// 表示无法创建该线程
		printf("can't create thread in current application!");
		exit(-1);
	

	// 表现为join操作,即线程必定阻塞主线程执行,&res表示将返回结果拿到
	//pthread_join(th, res);


执行结果(有部分情况下会出现如下效果,多次测试后才会出现)

并发就是有竞争,导致得到的结果不正确,这里结果应该是20

3. 将锁控制打开后

一般对于竞争的变量加入读保护和写保护就可以了(防止并发中写入或者读取不正确)

#include <stdio.h>
#include <stdlib.h>
// 从unistd中导入sleep函数
#include<unistd.h>
// 导入需要使用的线程库
#include <pthread.h>
/**
 * 	@description 当前内容主要为测试多线程访问同一个变量的操作,测试并发问题
 * 	@author hy
 * 	@createTime 2022-04-16
 */

void startThread(void *(*run)(void *), void *arg);
int shutdownNum = 3;
static int modifyNum = 0; // 一个全局的共享变量
int defaultSleepTime = 50;
int executeNum = 5;
// 定义互斥锁标记
pthread_mutex_t lock;

void lockAndIncreament() 
	pthread_mutex_lock(&lock);
	modifyNum += 1;
	pthread_mutex_unlock(&lock);


// 这里表示一个线程
void* run1(void* arg) 
	char* thread_name = NULL;
	// 校验参数数量和参数个数
	if (!arg || arg <= 0) 
		printf("exit with error!\\n");
		shutdownNum--;
		// 将参数的值进行赋值操作
		pthread_exit("arg len <= 0");
	 else 
		thread_name = ((char**) arg)[0];
	
	int num = 0;
	// 执行特定次打印就结束
	while (num < executeNum) 
		lockAndIncreament();
		//modifyNum+=1;
		++num;
		pthread_mutex_lock(&lock);
		printf("print %s : modifyNum : %d\\n", thread_name, modifyNum);
		fflush(stdout);
		pthread_mutex_unlock(&lock);
		// 表示使用打印为1秒每次sleep(1);
		sleep(1); //表示休眠200毫秒
	
	printf("%s exit!\\n", thread_name);
	fflush(stdout);
	shutdownNum--;
	pthread_exit("num >5");
	return NULL;


void* run2(void* arg) 
	char* thread_name = NULL;
	// 校验参数数量和参数个数
	if (!arg || arg <= 0) 
		printf("exit with error!\\n");
		shutdownNum--;
		// 将参数的值进行赋值操作
		pthread_exit("arg len <= 0");
	 else 
		thread_name = ((char**) arg)[0];
	
	int num = 0;
	// 执行特定次打印就结束
	while (num < executeNum) 
		lockAndIncreament();
		//modifyNum+=1;
		++num;
		pthread_mutex_lock(&lock);
		printf("print %s : modifyNum : %d\\n", thread_name, modifyNum);
		fflush(stdout);
		pthread_mutex_unlock(&lock);

		// 表示使用打印为1秒每次sleep(1);
		sleep(1); //表示休眠200毫秒
	
	printf("%s exit!\\n", thread_name);
	fflush(stdout);
	shutdownNum--;
	pthread_exit("num >5");
	return NULL;

void* run3(void* arg) 
	// 得到当前执行的线程的执行id
	char* thread_name = NULL;
	// 校验参数数量和参数个数
	if (!arg || arg <= 0) 
		printf("exit with error!\\n");
		shutdownNum--;
		// 将参数的值进行赋值操作
		pthread_exit("arg len <= 0");
	 else 
		thread_name = ((char**) arg)[0];
	
	int num = 0;
	// 执行特定次打印就结束
	while (num < executeNum) 
		lockAndIncreament();
		//modifyNum+=1;
		++num;
		pthread_mutex_lock(&lock);
		printf("print %s : modifyNum : %d\\n", thread_name, modifyNum);
		fflush(stdout);
		pthread_mutex_unlock(&lock);

		// 表示使用打印为1秒每次sleep(1);
		sleep(1); //表示休眠200毫秒
	
	printf("%s exit!\\n", thread_name);
	fflush(stdout);
	shutdownNum--;
	pthread_exit("num >5");
	return NULL;


void* run4(void* arg) 

	char* thread_name = NULL;
	// 校验参数数量和参数个数
	if (!arg || arg <= 0) 
		printf("exit with error!\\n");
		shutdownNum--;
		// 将参数的值进行赋值操作
		pthread_exit("arg len <= 0");
	 else 
		thread_name = ((char**) arg)[0];
	
	int num = 0;
	while (num < executeNum) 
		lockAndIncreament();
		//modifyNum+=1;
		++num;
		pthread_mutex_lock(&lock);
		printf("print %s : modifyNum : %d\\n", thread_name, modifyNum);
		fflush(stdout);
		pthread_mutex_unlock(&lock);

		// 表示使用打印为1秒每次sleep(1);
		sleep(1); //表示休眠200毫秒
	
	printf("%s exit!\\n", thread_name);
	fflush(stdout);
	shutdownNum--;
	pthread_exit("num >5");
	return NULL;


int main(int argc, char **argv) 
	void *(*func1)(void *) = run1;
	void *(*func2)(void *) = run2;
	void *(*func3)(void *) = run3;
	void *(*func4)(void *) = run4;
	char* arg1[2] =  "thread-1" ;
	void* args1 = arg1; // 使用携带正确参数的可以执行,否则认为不可执行
	printf("before create thread\\n");
	char* arg2[2] =  "thread-2" ;
	void* args2 = arg2;
	char* arg3[2] =  "thread-3" ;
	void* args3 = arg3;
	char* arg4[2] =  "thread-4" ;
	void* args4 = arg4;
	以上是关于C语言之简单使用互斥锁实现并发控制操作的主要内容,如果未能解决你的问题,请参考以下文章

C语言之简单使用互斥锁条件锁实现生产者消费者模型操作

Go并发编程之传统同步—互斥锁

GO语言并发编程之互斥锁读写锁详解

Go语言自学系列 | golang并发编程之Mutex互斥锁实现同步

GO 互斥锁实现原理剖析

GO 互斥锁实现原理剖析