多线程同步
信号量(semaphore),重点在信号,是一种信号机制 —“我已经把事情干好了,下面该你了”。典型的生产者–消费者模型。是协调任务执行顺序的一种机制。
比如有两个任务 A 和 B。任务 A 在执行两个数的加法运算,任务 B 需要用任务 A 运算的结果去执行乘法运算。此时,在任务 A 没有完成之前,任务 B 必须等待。当任务 A 完成后,使用信号量通知任务 B 去取结果。互斥量(mutex),重点在互斥,是一种锁机制——”现在这个东西归我,等我用完你们才能用,现在你们都得等着“。
比如有两个任务 A 和 B,一个文件描述符。A 和 B 都向文件中写入数据。如果同时写入,那么会导致文件内容紊乱,此时就需要锁机制。对文件描述符加锁。占有锁的任务可以执行写入操作。如果另一个任务也想写入数据,那么它必须先获得锁。如果测试锁被占用,那么它必须等待锁可用后才能写入数据。
信号量
#include<semaphore.h>
int sem_init(sem_t* sem, int pshared, unsigned int value);
/*
功能:信号量创建
参1:信号量;
参2: 0表示当前进程的局部信号量
参3:信号量的初始值
*/
int sem_destroy(sem_t* sem);
/*
功能:信号量销毁
*/
int sem_wait(sem_t* sem);
/*
功能:信号量减一(阻塞)
*/
int sem_trywait(sem_t* sem);
/*
功能:信号量减一(非阻塞)
*/
int sem_post(sem_t* sem);
/*
功能:信号量加一
*/
//上面这些函数成功返回0,失败返回-1,并设置errno
互斥量
基本函数
#include<pthread.h>
int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* mutexattr);
/*
功能:信号量加一
*/
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);
互斥锁属性
#include<pthread.h>
int pthread_mutexattr_init(pthread_mutexattr_t* attr);
int pthread_mutexattr_destroy(pthread_mutexattr_t* attr);
int pthread_mutexattr_getpshared(const pthread_mutexattr_t* attr, int* pshared);
int pthread_mutexattr_setpshared(pthread_mutexattr_t* attr, int pshared);
int pthread_mutexattr_gettype(const pthread_mutexattr_t* attr, int* type);
int pthread_mutexattr_settype(pthread_mutexattr_t* attr, int type);
条件变量
互斥锁存在的问题:明显的缺点就是它只有锁定和非锁定两种状态,多个线程同时访问一个共享资源,并不知道合适应该使用共享资源,在临界区增加判断语句,效率不高且难实现,而条件变量能在条件成立时触发相应的线程,进行变量的修改和访问
#include<pthread.h>
pthread_cond_t cond;
int pthread_cond_init(pthread_cond_t* cond, const pthread_condattr_t* cond_attr);
/*
功能:初始化
*/
int pthread_cond_destroy(pthread_cond_t* cond);
/*
功能:销毁
*/
int pthread_cond_broadcast(pthread_cond_t* cond);
/*
功能:激活所有等待的线程
*/
int pthread_cond_signal(pthread_cond_t* cond);
/*
功能:激活一个等待等待该条件的线程
*/
int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex);
/*
功能:只要到这个函数,就发生阻塞,直到使用pthread_cond_signal或者pthread_cond_broadcast给条件变量发送信号,此时该线程才继续运行,当被唤醒,pthread_cond_wait函数返回时,解除阻塞并重新申请获取互斥锁pthread_mutex_lock(&mutex);
*/
条件变量属性
#include <pthread.h>
int pthread_condattr_init(pthread_condattr_t* attr);
int pthread_condattr_destroy(pthread_condattr_t* attr);
//返回值:成功返回0;失败返回错误编码
int pthread_condattr_setshared(pthread_condattr_t* attr,int pshared);
//功能:设置条件变量的进程共享属性
int pthread_condattr_getshared(const pthread_condattr_t* restrict attr,int* restrict pshared);
//功能:获取条件变量的进程共享属性
int pthread_condattr_setclock(pthread_condattr_t* attr,clockid_t clock_id);
//功能:此函数用于设置pthread_cond_timewait函数使用的时钟ID
int pthread_condattr_getclock(const pthread_condattr_t* restrict attr,clockid_t *restrict clock_id);
//功能:此函数获取可被用于pthread_cond_timedwait函数的时钟ID。pthread_cond_timedwait函数使用前需要用pthread_condattr_t对条件变量进行初始化
扩展一
//扩展一 时间条件
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex,
const struct timespec *restrict abstime);
/*
功能:在限定的时间内等待
可以设置超时自动唤醒。sleep的缺陷是当有紧急事件到达时,线程无法及时唤醒
abstime参数:超时时间是一个绝对值,也就是距离1970-1-1 日的时间值
*/
//时间结构体
struct timespec
{
time_t tv_sec; /* Seconds. */
long tv_nsec; /* Nanoseconds. */
};
//时间获取函数
#include<time.h>
int clock_gettime(clockid_t clk_id,struct timespec *tp);
//参1: clk_id 获取和设置指定时钟时间的 clk_id;
//CLOCK_REALTIME:系统相对时间,从UTC 1970-1-1 0:0:0开始计时,更改系统时间会更改获取的值;
//CLOCK_MONOTONIC:系统绝对时间/单调时间,为系统重启到现在的时间,更改系统时间对它没有影响;
//CLOCK_PROCESS_CPUTIME_ID:本进程到当前代码系统CPU花费的时间;
//CLOCK_THREAD_CPUTIME_ID:本线程到当前代码系统CPU花费的时间;