线程同步机制(一)
- 资源竞争
- 互斥
- 死锁
- 信号量
- 条件变量
资源竞争
存在一个任务队列,多个并发线程同时处理这些任务。每个线程在完成某项任务后,检查任务队列中是否有新任务。如果有就处理该任务,并将该任务从任务队列中删除
假设:两个线程碰巧完成各自任务,但队列中只有一个任务。
可能发生的情况:第一个线程发现任务队列非空,准备接收该任务,但没有完成全部设置。此时,操作系统碰巧中断该线程。第二个线程获得了执行权,也发现任务队列非空,同样准备接收该任务,但发现已经无法正确设置任务队列。
最坏情况:第一个线程已经从任务队列中摘取了任务,但是还没有将任务队列设置为空,第二个线程对任务队列的访问导致段错误,系统崩溃。
互斥
互斥(mutex)定义与性质:mutial exclusion
- 相互独占锁,与二元信号量类似
- 一次只有一个线程可以锁定一个数据对象,并访问
- 只有该线程释放锁定,其他线程才能访问该数据对象
pthread_mutex_init()
函数:初始化互斥
-
原型:
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_attr_t *mutexattr);
-
参数:
mutex
为互斥对象,mutexattr
为互斥属性对象,NULL
表示使用缺省属性 -
可使用预定义宏
PTHREAD_MUTEX_INITIALIZER
初始化互斥
pthread_mutex_destroy()
函数:销毁互斥
-
原型:
int pthread_mutex_destroy(pthread_mutex_t *mutex);
pthread_mutex_lock()
函数:互斥加锁
-
原型:
int pthread_mutex_lock(pthread_mutex_t *mutex);
-
如果无法锁定,则调用阻塞,至该互斥被解除锁定状态
pthread_mutex_trylock()
函数:互斥加锁
-
原型:
int pthread_mutex_trylock(pthread_mutex_t *mutex);
-
如果无法锁定,则立即返回,不阻塞
pthread_mutex_unlock()
函数:互斥解锁
-
原型 :
int pthread_mutex_unlock(pthread_mutex_t *mutex);
使用互斥的流程
- 定义
pthread_mutex_t
类型的变量,将其地址作为第一个参数传给pthread_mutex_init()
函数;初始化函数只需调用一次 - 锁定或尝试锁定该互斥;获得访问权后,执行正常程序代码;并在执行完毕后解锁
互斥属性
-
pshared
属性:进程共享属性取值:
PTHREAD_PROCESS_SHARED
(跨进程共享)
PTHREAD_PROCESS_PRIVATE
(本进程内部共享) -
type
属性:互斥类型PTHREAD_MUTEX_NORMAL
:普通锁- 被某个线程锁定后,其他请求加锁的进程将等待
- 容易导致死锁
- 解锁被其他线程锁定或已解锁的互斥,将导致不可预期的后果
PTHREAD_MUTEX_ERRORCHEACK
:检错锁- 线程对已被其他线程锁定的互斥加锁,将返回EDEADLK
PTHREAD_MUTEX_RECURSIVE
:递归锁*- 允许线程对互斥多次加锁;解锁次数必须与加锁次数匹配
PTHREAD_MUTEX_DEFAULT
:默认锁- 实现上可能为上述三种之一
互斥属性函数
int pthread_mutexattr_init(pthread_mutexattr_t *attr); /*初始化互斥属性对象*/
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr); /*销毁互斥属性对象*/
int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind); /*设置typed属性*/
int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *kind); /*获取type属性*/
int pthread_mutexattr_getpshared(const pthread_mutexattr_t*attr, int *pshared);/*获取pshared属性*/
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr,int pshared); /*获取pshared属性*/
互斥程序例子
#include <list> #include <iostream> #include <pthread.h> using namespace std; struct Job { Job (int x = 0, int y = 0) : x (x), y (y) { } int x, y; }; list<Job *> job_queue; pthread_mutex_t job_queue_mutex = PTHREAD_MUTEX_INITIALIZER; void ProcessJob (Job *job) { cout << "Thread " << (int) pthread_self (); cout << "processing (" << job->x << "," << job->y << ")\n"; } //处理作业时需要加锁 void *DequeueJob (void *arg) { while (true) { Job *job = nullptr; pthread_mutex_lock (&job_queue_mutex); if (!job_queue.empty ()) { job = job_queue.front (); job_queue.pop_front (); } pthread_mutex_unlock (&job_queue_mutex); if (!job) { break; } ProcessJob (job); delete job, job = nullptr; } return nullptr; } void *EnqueueJob (void *arg) { Job *job = reinterpret_cast<Job *>(arg); pthread_mutex_lock (&job_queue_mutex); job_queue.push_back (job); cout << "Thread " << (int) pthread_self (); cout << " enqueueing (" << job->x << "," << job->y << ")\n"; pthread_mutex_unlock (&job_queue_mutex); return nullptr; } int main () { int i; pthread_t threads[8]; for (i = 0; i < 5; ++i) { Job *job = new Job (i + 1, i + 2); pthread_create (&threads[i], nullptr, EnqueueJob, job); } for (i = 5; i < 8; ++i) { pthread_create (&threads[i], nullptr, DequeueJob, nullptr); } for (i = 0; i < 8; ++i) { pthread_join (threads[i], nullptr); } return 0; }