Unix C语言 POSIX 互斥锁 线程同步
文章目录
- 模拟并发冲突
- 出现该问题的原因
- POSIX的互斥锁
由于线程的并发性,导致多个线程同时对同一个全局变量进行操作时,会出现数据不一致的情况。
所以需要在线程中对全局变量的操作上互斥锁,来确保该操作同一时刻只能被一个线程进行,从而确保了数据安全。
模拟并发冲突
先不着急看互斥锁,先看下互斥锁可以解决什么问题。
下面的代码有个int型的num全局变量,开启多个线程同时对该num进行+1的动作,每个线程+1执行1000000次,所有线程执行完毕后,num的值应该等于线程数*1000000
。但由于并发执行,结果是小于线程数*1000000
的。
可以自行跑代码。
注意编译需要链接pthread库:gcc ... -lpthread
#include <pthread.h>
#include <stdio.h>
#include <string.h>
int num = 0; //全局变量
/** * 函数执行体。类似java中的Thread#run函数。 * 当函数体执行完毕,则线程结束。 * @param arg 创建线程时传入的参数。 * @return 线程返回的结果,可以被创建该线程的父线程通过pthread_join获取。 */
void *thread_proc(void *arg) {
for (int i = 0; i < 1000000; ++i) {
num++;
}
return NULL;
}
int main(void) {
int tcount = 3; //线程数
pthread_t tids[tcount];
for (long long i = 1; i <= tcount; ++i) { //创建多个线程
int errno = pthread_create(&tids[i - 1], NULL, thread_proc, (void *) i); //创建线程
if (errno) { //当线程创建失败,返回的errno非0
fprintf(stderr, "pthread_create:%s\n", strerror(errno)); //打印异常信息
return -1;
}
}
for (int i = 0; i < tcount; ++i) {
pthread_t tid = tids[i];
pthread_join(tid, NULL);
printf("线程%lld结束。\n", (long long) tid);
}
printf("所有线程执行完毕,num=%d\n", num);
getchar();//防止主线程退出,回车即退出。
return 0;
}
出现该问题的原因
代码中的num++
虽说是一行代码,但实际对于CPU来说是3个指令操作:将num从内存加载到CPU寄存器、寄存器num+1、寄存器num+1后的值写入内存。
由于多个线程同时并发,并错落执行对num的操作指令,CPU来回切换的线程上下文中的num与内存中的num会不一致,进而导致最终的num并非是按照循环次数的正确数字。
POSIX的互斥锁
要解决上面的问题,那就要对线程中存在并发操作共享数据的代码进行上锁,使其操作线程安全。
互斥锁的API和线程相关的API一样,都在<pthread.h>
这个头文件中。
创建互斥锁有两种方式,一种是静态创建,另一种是动态创建:
-静态创建pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
-动态创建int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
:动态创建互斥锁int pthread_mutex_destroy(pthread_mutex_t *mutex);
:释放动态创建的互斥锁资源
-上锁解锁int pthread_mutex_lock(pthread_mutex_t *mutex);
:上互斥锁int pthread_mutex_unlock(pthread_mutex_t *mutex);
:解互斥锁
基于前面的问题代码进行上锁操作,会发现最终的num等于线程数*1000000
:
#include <pthread.h>
#include <stdio.h>
#include <string.h>
int num = 0; //全局变量
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //静态创建互斥锁
/** * 函数执行体。类似java中的Thread#run函数。 * 当函数体执行完毕,则线程结束。 * @param arg 创建线程时传入的参数。 * @return 线程返回的结果,可以被创建该线程的父线程通过pthread_join获取。 */
void *thread_proc(void *arg) {
for (int i = 0; i < 1000000; ++i) {
pthread_mutex_lock(&mutex); //上锁
num++;
pthread_mutex_unlock(&mutex); //解锁
}
return NULL;
}
int main(void) {
int tcount = 3;
pthread_t tids[tcount];
for (long long i = 1; i <= tcount; ++i) { //创建多个线程
int errno = pthread_create(&tids[i - 1], NULL, thread_proc, (void *) i); //创建线程
if (errno) { //当线程创建失败,返回的errno非0
fprintf(stderr, "pthread_create:%s\n", strerror(errno)); //打印异常信息
return -1;
}
}
for (int i = 0; i < tcount; ++i) {
pthread_t tid = tids[i];
pthread_join(tid, NULL);
printf("线程%lld结束。\n", (long long) tid);
}
printf("所有线程执行完毕,num=%d\n", num);
getchar();//防止主线程退出,回车即退出。
return 0;
}
还没有评论,来说两句吧...