当前版本:eclipse c++
、MinGW-W64-builds-4.3.5
、widnwos
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. 一般对于全局的变量的读写操作都是需要加锁的,加锁不正确可能导致结果不正确(多次测试才会出现这个问题)