赞
踩
互斥锁是一种使用频繁的同步手段,也被称为互斥量。对比信号量的使用,我们可以将互斥锁的使用理解为信号量初值仅为1的一种情况。
互斥锁是属于系统的内核级对象,它能够使线程拥有某个资源的绝对访问权,互斥锁主要包括使用数量、线程ID,递归计数器等,其中线程ID表示当前拥有互斥锁的线程,递归计数器表示线程拥有互斥锁的次数。
互斥锁可以进行分类:
分类 | 函数定义 |
---|---|
静态分配互斥锁 | pthread_mutex_t mutex= PTHREAD_MUTEX_INITIALIZER; |
动态分配互斥锁 | pthread_mutex_init(&mutex, *mutexattr);pthread_mutex_destroy(&mutex); |
每一个互斥锁都有一个数据类型为pthread_mutex_t
的互斥变量,表格中的静态分配互斥锁中的PTHREAD_MUTEX_INITIALIZER
是一个结构常量,所以是可以直接创建一个互斥锁用该常量进行初始化。
或者就使用动态的方式初始化一个互斥锁,这种方式的互斥锁在使用结束后一定要进行销毁。其中初始化的pthread_mutex_init
的参数中需要传递一个mutexattr
的属性值,而这种锁的属性共有四种。
属性值 | 意义 |
---|---|
PTHREAD_MUTEX_TIMED_NP | 这是缺省值,相当于NULL。当一个线程加锁后,其他请求这个锁的线程会进入一个队列等待,锁释放后按队列优先级获得锁。具有公平性。 |
PTHREAD_MUTEX_RECURSIVE_NP | 嵌套锁,允许同一个线程多次获得同一个锁,并多次解锁 |
PTHREAD_MUTEX_ERRORCHECK_NP | 检错锁,如果同一个线程请求同一个锁,返回EDEADLK;负责和PTHREAD_MUTEX_TIMED_NP操作一样(进入等待队列) |
PTHREAD_MUTEX_ADAPTIVE_NP | 适应锁,如果被锁上了,等解锁后重新竞争,不存在等待队列,而是解锁后先到先得。 |
操作 | 函数 | 意义 |
---|---|---|
阻塞加锁 | int pthread_mutex_lock(pthread_t *mutex) | 锁空闲,立即加锁否则阻塞等待直至解锁 |
非阻塞加锁 | int pthread_mutex_trylock(pthread_t *mutex) | 锁空闲,立即加锁;否则立即返回 EBUSY而非等待 |
避免死锁的加锁 | int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timesec *restrict tsptr); | 可设置等待时间,若超时锁未释放,返回ETIMEDOUT,避免两个或多个线程加了锁,又彼此等待锁的释放而产生死锁。 |
解锁 | int pthread_mutex_unlock(pthread_t *mutex) | 有加锁必然有解锁 |
销毁锁 | int pthread_mutex_destroy(pthread_mutex *mutex); | 互斥锁也是一种资源,使用完毕后需要释放 |
代码如下:
#include <iostream> #include <pthread.h> #include <unistd.h> #include <semaphore.h> using namespace std; pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; void* func(void* arg){ sem_t* p = (sem_t*)arg; for(int i=0; i<10; ++i){ usleep(100); pthread_mutex_lock(&m); cout << pthread_self() << ":before" << endl; usleep(100); cout << pthread_self() << ":exit" << endl; pthread_mutex_unlock(&m); } } int main(){ pthread_t tid; pthread_create(&tid, NULL, func, NULL); usleep(500); // 等待子进程跑起来 func(NULL); pthread_join(tid, NULL); }
执行部分结果如下:
#include <iostream> #include <pthread.h> #include <unistd.h> #include <semaphore.h> using namespace std; void* func(void* arg){ pthread_mutex_t* p = (pthread_mutex_t*)arg; for(int i=0; i<10; ++i){ usleep(100); pthread_mutex_lock(p); cout << pthread_self() << ":before" << endl; usleep(100); cout << pthread_self() << ":exit" << endl; pthread_mutex_unlock(p); } } int main(){ pthread_mutex_t m; pthread_mutex_init(&m, NULL); pthread_t tid; pthread_create(&tid, NULL, func, &m); usleep(500); // 等待子进程跑起来 func(&m); pthread_join(tid, NULL); pthread_mutex_destroy(&m); }
执行部分结果如下:
显然这两种方式的互斥锁都保证在临界区内只有一个线程运行,保证了线程的同步。
这里我们直接使用标准库初始化一个互斥锁,进行加锁和解锁,具体代码如下:
#include <iostream> #include <thread> #include <mutex> using namespace std; int main(){ mutex m; auto func = [&m](){ // 互斥锁是一种资源,不能拷贝,只能移动 for(int i=0; i<5; i++){ m.lock(); // 加锁 cout << this_thread::get_id() << ":before" << endl; this_thread::sleep_for(500ms); cout << this_thread::get_id() << ":after" << endl; m.unlock(); // 解锁 } }; thread t(func); func(); t.join(); }
除此以外,我们可以不使用函数lock()
和unlock()
函数,而是直接使用lock_guard
来实现加锁和解锁的函数,具体代码如下:
#include <iostream> #include <thread> #include <mutex> using namespace std; int main(){ mutex m; auto func = [&m](){ for(int i=0; i<5; i++){ lock_guard<mutex> guard(m); cout << this_thread::get_id() << ":before" << endl; this_thread::sleep_for(500ms); cout << this_thread::get_id() << ":after" << endl; } }; thread t(func); func(); t.join(); }
其中的lock_guard<mutex>
是守护互斥锁,借助一个对象在离开其作用域时将会自发调用析构函数这个特点,以上的代码块当守护锁离开其所在的作用域时将会解锁。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。