赞
踩
如何设置获取互斥量时的等待时间?
如果等待超时,如何避免死锁?
解决方案:
1、尝试获取第 1 个互斥量:
2、尝试在规定时间内获取第 2 个互斥量:
test-1.c
- #define _GNU_SOURCE /* To get pthread_getattr_np() declaration */
- #include <pthread.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <memory.h>
- #include <time.h>
-
- pthread_mutex_t m1 = PTHREAD_MUTEX_INITIALIZER;
- pthread_mutex_t m2 = PTHREAD_MUTEX_INITIALIZER;
-
- int g_count = 0;
-
- void live_lock(pthread_mutex_t* pm[], int n)
- {
- int r = -1;
- int i = 0;
- int j = 0;
- struct timespec tout = {0};
-
- while( r )
- {
- for(i=0; i<n; i++)
- {
- clock_gettime(CLOCK_REALTIME, &tout);
-
- tout.tv_sec += 1;
-
- r = pthread_mutex_timedlock(pm[i], &tout);
-
- if( r )
- {
- for(j=0; j<i; j++)
- {
- pthread_mutex_unlock(pm[j]);
- }
- usleep(100 * 1000);
- break;
- }
- }
- }
- }
-
- void live_unlock(pthread_mutex_t* pm[], int n)
- {
- int i = 0;
-
- while( i < n )
- {
- pthread_mutex_unlock(pm[i++]);
- }
- }
-
- void* thread_1(void* arg)
- {
- pthread_mutex_t* pm[] = {&m1, &m2};
- int n = sizeof(pm)/sizeof(*pm);
-
- while(1)
- {
- live_lock(pm, n);
-
- printf("%s : %d\n", __FUNCTION__, g_count++);
-
- usleep(100);
-
- live_unlock(pm, n);
- }
-
- pthread_detach(pthread_self());
-
- return NULL;
- }
-
- void* thread_2(void* arg)
- {
- pthread_mutex_t* pm[] = {&m2, &m1};
- int n = sizeof(pm)/sizeof(*pm);
-
- while(1)
- {
- live_lock(pm, n);
-
- printf("%s : %d\n", __FUNCTION__, g_count++);
-
- usleep(100);
-
- live_unlock(pm, n);
- }
-
- pthread_detach(pthread_self());
-
- return NULL;
- }
-
- int main()
- {
- int r = 0;
- pthread_t t;
-
- pthread_create(&t, NULL, thread_1, NULL);
- pthread_create(&t, NULL, thread_2, NULL);
-
- printf("Hello World!\n");
-
- while( 1 )
- {
- sleep(1);
- }
-
- return 0;
- }
live_lock() 函数是我们实现的活锁解决方案,这个函数支持任意把锁,我们这里设置的超时时间是 1s,如果 1s 内没有获取到锁,则释放已经获取到的锁
在 mian() 函数中,我们创建两个线程,thread_1 先获取互斥量 m1,再获取互斥量 m2,而 thread_2 先获取互斥量 m2,再获取互斥量 m1,他们获取互斥量存在一个环路,可能会导致死锁,而这里使用的活锁方案就可以避免死锁
程序运行结果如下图所示:
当 thread_1 获取到 互斥量 m1,thread_2 获取到互斥量 m2,其中一个互斥量会由于在超时时间内抢夺不到互斥锁而放弃之前获取到的锁,这样另一个线程就能获取到锁,访问临界区
线程获取互斥量失败后究竟发生了什么?
线程会进入阻塞状态,让出 cpu 的执行权,等到互斥量被释放,线程才有机会得到执行
自旋锁也是一种用于保证临界区的原子性的机制
自旋锁与互斥量类似,在任何时刻,最多只能有一个持有者
自旋锁与互斥量在内部机制上不同:
应用场景
实现机制
自旋锁类型:
test-2.c
- #define _GNU_SOURCE /* To get pthread_getattr_np() declaration */
- #include <pthread.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <memory.h>
- #include <time.h>
-
- int g_count = 0;
- pthread_mutex_t g_mutex;
- pthread_spinlock_t g_spin;
-
- void* thread_1(void* arg)
- {
- while(1)
- {
- pthread_spin_lock(&g_spin);
- // pthread_mutex_lock(&g_mutex);
-
- printf("%s : %d\n", __FUNCTION__, g_count++);
-
- usleep(100 * 1000);
-
- pthread_spin_unlock(&g_spin);
- // pthread_mutex_unlock(&g_mutex);
- }
-
- pthread_detach(pthread_self());
-
- return NULL;
- }
-
- void* thread_2(void* arg)
- {
- while(1)
- {
- pthread_spin_lock(&g_spin);
- // pthread_mutex_lock(&g_mutex);
-
- printf("%s : %d\n", __FUNCTION__, g_count++);
-
- usleep(100 * 1000);
-
- pthread_spin_unlock(&g_spin);
- // pthread_mutex_unlock(&g_mutex);
- }
-
- pthread_detach(pthread_self());
-
- return NULL;
- }
-
- int main()
- {
- int r = 0;
- pthread_t t;
-
- pthread_mutexattr_t mattr;
-
- pthread_mutexattr_init(&mattr);
- pthread_mutex_init(&g_mutex, &mattr);
-
- pthread_spin_init(&g_spin, PTHREAD_PROCESS_PRIVATE);
-
- pthread_create(&t, NULL, thread_1, NULL);
- pthread_create(&t, NULL, thread_2, NULL);
-
- printf("Hello World!\n");
-
- while( 1 )
- {
- // printf("%s : %d\n", __FUNCTION__, g_count);
- sleep(1);
- }
-
- return 0;
- }
程序运行结果如下图所示:
程序正常正确的运行了,不会产生死锁
我们使用 echo $$ 命令,查看 shell 进程的 pid
shell 进程的 pid 为 2274
使用 sudo chrt -p -f 10 2274 命令,改变 shell 的调度策略为 FIFO,这样在 shell 上执行的进程的默认都是实时进程,进程调度策略为 FIFO
最后使用 taskset -c 0 ./a.out 命令,指定这个程序在 cpu0 上执行
程序运行结果如下图所示:
程序发生了死锁,a.out 程序 cpu 占用率快达到 100%
这是因为 thread_1 优先获取到执行权,获取到自旋锁,打印后,通过 usleep() 主动释放 cpu 资源,调度到 thread_2 时,thread_2 会一直获取不到自旋锁,原地自旋,一直消耗cpu 资源,由于进程的调度策略是 FIFO,只有 thread_2 主动释放 cpu 资源,cpu0 才能调度其他线程执行,由于 thread_2 一直无法获取到自旋锁,所以这个程序产生了死锁,并且 thread_2 一直在消耗 cpu 资源
我们使用互斥锁,将自旋锁的代码注释掉,互斥锁的代码打开,再重复进行一次上面的实验
程序没有发生死锁,并且 cpu 的占用率很低
这是因为 thread_2 获取不到互斥锁,会进入阻塞状态,让出 cpu 资源,从而 cpu0 可以调度 thread_1 执行
轻量级锁定,即:临界区相对短小,自旋锁持有时间非常短
同一线程不可重复获取自旋锁 (导致死锁)
如果只有一个单核处理器,不建议使用自旋锁
线程一旦获取自旋锁,则不能让出处理器使用权
即:线程 获取锁 到 释放锁 的时间内只有一个执行流
存在一种 "锁" 类型:
一种特殊的互斥量,又名:自适应锁
自适应锁先以自旋的方式持续尝试获取目标锁
当超时未能获取目标锁,则让出处理器使用权,线程进入阻塞状态
自适应锁相对普通互斥量效率更高,相对自旋锁安全性更好
test-3.c
- #define _GNU_SOURCE /* To get pthread_getattr_np() declaration */
- #include <pthread.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <memory.h>
- #include <time.h>
-
- int g_count = 0;
- pthread_mutex_t g_mutex;
-
- void* thread_1(void* arg)
- {
- while(1)
- {
- pthread_mutex_lock(&g_mutex);
-
- printf("%s : %d\n", __FUNCTION__, g_count++);
-
- usleep(100 * 1000);
-
- pthread_mutex_unlock(&g_mutex);
- }
-
- pthread_detach(pthread_self());
-
- return NULL;
- }
-
- void* thread_2(void* arg)
- {
- while(1)
- {
- pthread_mutex_lock(&g_mutex);
-
- printf("%s : %d\n", __FUNCTION__, g_count++);
-
- usleep(100 * 1000);
-
- pthread_mutex_unlock(&g_mutex);
- }
-
- pthread_detach(pthread_self());
-
- return NULL;
- }
-
- int main()
- {
- int r = 0;
- pthread_t t;
-
- pthread_mutexattr_t mattr;
-
- pthread_mutexattr_init(&mattr);
- pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_ADAPTIVE_NP);
- pthread_mutex_init(&g_mutex, &mattr);
-
- pthread_create(&t, NULL, thread_1, NULL);
- pthread_create(&t, NULL, thread_2, NULL);
-
- printf("Hello World!\n");
-
- while( 1 )
- {
- // printf("%s : %d\n", __FUNCTION__, g_count);
- sleep(1);
- }
-
- return 0;
- }
第 56 行,将互斥锁的类型设置为自适应锁
程序运行结果如下图所示:
程序正常正确的运行了,不会发生死锁
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。