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;
	// 启动三个线程,执行不同的方法(如果执行的方法相同则不会出现并发?)
	// 解决多线程问题
	// 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);
}

多次测试后发现结果是正确的进行了有效的控制
在这里插入图片描述

4. 并发在C中的结论

1.对于c中的线程互斥并发控制需要控制好粒度,注意对于printf和fflush的操作也会导致并发打印出现问题的情况

2. 通过初始化锁、加锁、解锁、销毁锁的操作可以对c中的互斥锁实现控制

3. 一般对于全局的变量的读写操作都是需要加锁的,加锁不正确可能导致结果不正确(多次测试才会出现这个问题)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值