互斥锁实现多线程同步的核心思想是:有线程访问进程空间中的公共资源时,该线程执行“加锁”操作(将资源“锁”起来),阻止其它线程访问。访问完成后,该线程负责完成“解锁”操作,将资源让给其它线程。当有多个线程想访问资源时,谁最先完成“加锁”操作,谁就最先访问资源。有关信号量、条件变量以及读写锁实现线程同步,感兴趣的读者可前往《多线程编程(C语言+Linux)》做系统性地学习。
本质上,互斥锁就是一个全局变量,它只有 "lock" 和 "unlock" 两个值,含义分别是:当有多个线程想访问“加锁”状态下的公共资源时,它们只能等待资源“解锁”,所有线程会排成一个等待(阻塞)队列。资源解锁后,操作系统会唤醒等待队列中的所有线程,第一个访问资源的线程会率先将资源“锁”起来,其它线程则继续等待。
<pthread.h>
头文件中。举个例子:
pthread_mutex_t myMutex;我们成功地定义了一个名为 myMutex 的互斥锁,但要想使用它,还要进行初始化操作。
//1、使用特定的宏 pthread_mutex_t myMutex = PTHREAD_MUTEX_INITIALIZER; //2、调用初始化的函数 pthread_mutex_t myMutex; pthread_mutex_init(&myMutex , NULL);以上两种初始化方式是完全等价的,PTHREAD_MUTEX_INITIALIZER 宏和 pthread_mutex_init() 函数都定义在 <pthread.h> 头文件中,它们的主要区别在于:
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);mutex 参数表示要初始化的互斥锁;attr 参数用于自定义新建互斥锁的属性,attr 的值为 NULL 时表示以默认属性创建互斥锁。
注意,不能对一个已经初始化过的互斥锁再进行初始化操作,否则会导致程序出现无法预料的错误。
int pthread_mutex_lock(pthread_mutex_t* mutex); //实现加锁 int pthread_mutex_trylock(pthread_mutex_t* mutex); //实现加锁 int pthread_mutex_unlock(pthread_mutex_t* mutex); //实现解锁参数 mutex 表示我们要操控的互斥锁。函数执行成功时返回数字 0,否则返回非零数。
pthread_mutex_t myMutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); pthread_mutex_init(&myMutex , NULL);手动释放 myMutex 占用的内存(调用 free() 函数)之前,必须先调用 pthread_mutex_destory() 函数销毁该对象。
int pthread_mutex_destroy(pthread_mutex_t *mutex);参数 mutex 表示要销毁的互斥锁。如果函数成功销毁指定的互斥锁,返回数字 0,反之返回非零数。
注意,对于用 PTHREAD_MUTEX_INITIALIZER 或者 pthread_mutex_init() 函数直接初始化的互斥锁,无需调用 pthread_mutex_destory() 函数手动销毁。
#include<stdio.h> #include<stdlib.h> #include<pthread.h> #include<unistd.h> int ticket_sum = 10; //创建互斥锁 pthread_mutex_t myMutex = PTHREAD_MUTEX_INITIALIZER; //模拟售票员卖票 void *sell_ticket(void *arg) { //输出当前执行函数的线程 ID printf("当前线程ID:%u\n", pthread_self()); int i; int islock = 0; for (i = 0; i < 10; i++) { //当前线程“加锁” islock = pthread_mutex_lock(&myMutex); //如果“加锁”成功,执行如下代码 if (islock == 0) { //如果票数 >0 ,开始卖票 if (ticket_sum > 0) { sleep(1); printf("%u 卖第 %d 张票\n", pthread_self(), 10 - ticket_sum + 1); ticket_sum--; } //当前线程模拟完卖票过程,执行“解锁”操作 pthread_mutex_unlock(&myMutex); } } return 0; } int main() { int flag; int i; void *ans; //创建 4 个线程,模拟 4 个售票员 pthread_t tids[4]; for (i = 0; i < 4; i++) { flag = pthread_create(&tids[i], NULL, &sell_ticket, NULL); if (flag != 0) { printf("线程创建失败!"); return 0; } } sleep(10); //等待 4 个线程执行完成 for (i = 0; i < 4; i++) { //阻塞主线程,确认 4 个线程执行完成 flag = pthread_join(tids[i], &ans); if (flag != 0) { printf("tid=%d 等待失败!", tids[i]); return 0; } } return 0; }假设程序编写在 thread.c 文件中,执行过程为:
[root@localhost ~]# gcc thread.c -o thread.exe -lpthread
[root@localhost ~]# ./thread.exe
当前线程ID:149493504
当前线程ID:170473216
当前线程ID:159983360
当前线程ID:139003648
149493504 卖第 1 张票
149493504 卖第 2 张票
149493504 卖第 3 张票
139003648 卖第 4 张票
139003648 卖第 5 张票
139003648 卖第 6 张票
139003648 卖第 7 张票
139003648 卖第 8 张票
159983360 卖第 9 张票
159983360 卖第 10 张票
Copyright © 广州京杭网络科技有限公司 2005-2024 版权所有 粤ICP备16019765号
广州京杭网络科技有限公司 版权所有