- 1. 基本概念
- 2. 互斥锁
- 2.1 基本逻辑
- 2.2 函数接口
- 3. 读写锁
- 3.1 基本逻辑
- 加锁
- 解锁
互斥与同步是最基本的逻辑概念:
互斥指的是控制两个进度使之互相排斥,不同时运行。
同步指的是控制两个进度使之有先有后,次序可控。
同步与互斥
使得多线程间互斥运行的最简单办法,就是增加一个互斥锁。任何一条线成要开始运行互斥区间的代码,都必须先获取互斥锁,而互斥锁的本质是一个二值信号量,因此当其中一条线程抢先获取了互斥锁之后,其余线程就无法再次获取了,效果相当于给相关的资源加了把锁,直到使用者主动解锁,其余线程方可有机会获取这把锁。
定义
互斥锁是一个特殊的变量,定义如下:
#includepthread_mutex_t m;
一般而言,由于互斥锁需要被多条线程使用,因此一般会将互斥锁定义为全局变量。
初始化与销毁
未经初始化的互斥锁是无法使用的,初始化互斥锁有两种办法:
静态初始化
动态初始化
静态初始化很简单,就是在定义同时赋予其初值:
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
由于静态初始化互斥锁不涉及动态内存,因此无需显式释放互斥锁资源,互斥锁将会伴随程序一直存在,直到程序退出为止。而所谓动态初始化指的是使用 pthread_mutex_init() 给互斥锁分配动态内存并赋予初始值,因此这种情形下的互斥锁需要在用完之后显式地进行释放资源,接口如下:
#include// 初始化互斥锁 int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); // 销毁互斥锁 int pthread_mutex_destroy(pthread_mutex_t *mutex);
接口说明:
mutex:互斥锁
attr:互斥锁属性(一般设置为NULL)
加锁与解锁
互斥锁的基本 *** 作就是加锁与解锁,接口如下:
#includepthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; // 加锁 pthread_mutex_lock( &m ); // 解锁 pthread_mutex_unlock( &m );
示例代码
将此前判断偶数的代码用互斥锁加以改进如下:
// concurrency.c
#include#include #include #include #include #include pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; int global = 100; void *isPrime(void *arg) { while(1) { pthread_mutex_lock(&m); // 一段朴素的代码 if(global%2 == 0) printf("%d是偶数n", global); pthread_mutex_unlock(&m); } } int main() { pthread_t tid; pthread_create(&tid, NULL, isPrime, NULL); // 一条人畜无害的赋值语句 while(1) { pthread_mutex_lock(&m); global = rand() % 5000; pthread_mutex_unlock(&m); } }
运行结果如下:
gec@ubuntu:~$ ./concurrency
492是偶数
2362是偶数
2778是偶数
3926是偶数
540是偶数
3426是偶数
4172是偶数
112是偶数
368是偶数
2576是偶数
1530是偶数
1530是偶数
2862是偶数
4706是偶数
…
gec@ubuntu:~$
可见,有了互斥锁之后,输出的结果正确了。
对于互斥锁而言,凡是涉及临界资源的访问一律加锁,这在并发读 *** 作的场景下会大量浪费时间。要想提高访问效率,就必须要将对资源的读写 *** 作加以区分:读 *** 作可以多任务并发执行,只有写 *** 作才进行恰当的互斥。这就是读写锁的设计来源。
读写锁提高了资源访问的效率
定义
与互斥锁类似,读写锁也是一种特殊的变量:
pthread_rwlock_t rw;
初始化
与互斥锁类似,读写锁也分成静态初始化和动态初始化:
#include加锁// 静态初始化: pthread_rwlock_t rw = PTHREAD_RWLOCK_INITIALIZER; // 动态初始化与销毁: int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr); int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
读写锁最大的特点是对即将要做的读写 *** 作做了区分:
读 *** 作可以共享,因此多条线程可以对同一个读写锁加多重读锁
写 *** 作天然互斥,因此多条线程只能有一个拥有写锁。(注意写锁与读锁也是互斥的)
#inclue
// 读锁 // 1,阻塞版本 int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); // 2,非阻塞版本 int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); // 写锁 // 1,阻塞版本 int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); // 2,非阻塞版本 int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
*** 作原则:
如果只对数据进行读 *** 作,那么就加 → 读锁。
如果要对数据进行写 *** 作,那么就加 → 写锁。
#include
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)