一、互斥锁(互斥量)
互斥锁是一种特殊的变量,有上锁(lock)和解锁(unlock)两种状态。
当处于解锁状态时,线程想获取该互斥锁,就可以获取不被阻塞,互斥锁变为锁定状态;
当处于锁定状态时,线程获取互斥锁被阻塞,并加入到这个互斥锁的等待队列中。
互斥锁有点像打印机,空闲时你可以打印;别人在打印时,你就需要排队等待打印机空闲。
1.1 创建并初始化一个互斥锁
使用 pthread_mutex_t 类型的变量来表示互斥锁。在使用之前,必须对其进行初始化。
静态初始化:
pthread_mutex_t mutex =PTHREAD_MUTEX_INITIALIZER;
动态初始化:
pthread_mutex_init 函数
一般我们都使用静态初始化。
理由:
静态初始化通常比 pthread_mutex_init 更有效,而且可以在定义为全局变量时即完成初始化,这样可以保证在任何线程开始执行之前,初始化既已完成。
1.2 互斥锁相关属性及分类
//初始化互斥锁属性
//初始化互斥锁属性
pthread_mutexattr_init(pthread_mutexattr_t attr);//销毁互斥锁属性
pthread_mutexattr_destroy(pthread_mutexattr_t attr);//用于获取互斥锁属性
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *restrict attr ,int *restrict pshared);//用于设置互斥锁属性
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr ,int pshared);
attr表示互斥锁的属性,pshared表示互斥锁的共享属性,有两种取值:
1)PTHREAD_PROCESS_PRIVATE:锁只能用于一个进程内部的两个线程进行互斥(默认情况)
2)PTHREAD_PROCESS_SHARED:锁可用于两个不同进程中的线程进行互斥,使用时还需要在进程共享内存中分配互斥锁,然后该互斥锁指定属性就可以了。
1.3 互斥锁常用函数
// 销毁互斥锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);// 对互斥锁的锁定
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);// 对互斥锁的解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
pthread_mutex_lock函数会调用这个函数线程一直阻塞到互斥锁可用为止,而pthread_mutex_trylock会立即返回
注意事项:
-
尽量保持锁的粒度,越小越好。(访问共享数据,加锁。访问家属【立即】解锁。)
-
互斥锁本质是结构体,可以看成整数,初值为1。(当pthread_mutex_init 函数调用成功。)
-
加锁:--操作,阻塞线程。
-
解锁:++操作,唤醒阻塞在锁上的线程。
二、信号量
信号量广泛用于进程或线程间的同步或互斥,信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。
根据信号量的值来判断是否对公共资源具有访问权限,当信号量的值大于0时,可以访问,否则将阻塞。相当于初始化值为N的互斥量。N值,表示可以同时访问共享数据区的线程数。
带有两个原子操作 P 和 V,一次 P 操作使信号量减1,一次 V 操作使信号量加 1。
函数:
sem_t sem //定义类型
初始化信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数:
sem 信号量
pshared 0:用于线程间同步
1:用于进程间同步
value: N值:指定同时访问的线程数
#include <semaphore.h>// 信号量 P 操作(减 1)
int sem_wait(sem_t *sem);// 以非阻塞的方式来对信号量进行减 1 操作
int sem_trywait(sem_t *sem);// 信号量 V 操作(加 1)
int sem_post(sem_t *sem);// 获取信号量的值
int sem_getvalue(sem_t *sem, int *sval);// 销毁信号量
int sem_destroy(sem_t *sem);
// 信号量用于同步实例
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>sem_t sem_g,sem_p; //定义两个信号量
char ch = 'a';void *pthread_g(void *arg) //此线程改变字符ch的值
{while(1){sem_wait(&sem_g);ch++;sleep(1);sem_post(&sem_p);}
}void *pthread_p(void *arg) //此线程打印ch的值
{while(1){sem_wait(&sem_p);printf("%c",ch);fflush(stdout); // 刷新标准输出缓冲区sem_post(&sem_g);}
}int main(int argc, char *argv[])
{pthread_t tid1,tid2;sem_init(&sem_g, 0, 0); // 初始化信号量为0sem_init(&sem_p, 0, 1); // 初始化信号量为1// 创建两个线程pthread_create(&tid1, NULL, pthread_g, NULL);pthread_create(&tid2, NULL, pthread_p, NULL);// 回收线程pthread_join(tid1, NULL);pthread_join(tid2, NULL);return 0;
}