(一)临界资源的概念
临界资源是指一次只允许一个任务(进程、线程)访问的共享资源,具有独占性。
(二)临界区
临界区就是指访问临界资源的代码。
(三)互斥机制(互斥锁)
linux下提供一个互斥机制:mutex互斥锁,即任务(进程、线程)访问临界资源前申请锁,访问完后释放锁。
(四)互斥锁和信号量(灯)、互斥和同步的区别
信号量是一个线程完成了某一任务后,通过信号量告诉别的线程可以进行操作了。
互斥锁是用在多线程多任务互斥的,一个线程占用了某一个资源,那么别的线程就无法访问,直到这个线程unlock,其他的线程才开始可以利用这个资源。
也就是说,信号量强调的是线程执行的先后顺序;互斥锁强调独占性。
互斥锁和信号量两者之间的区别:
作用域:
信号量: 进程间或线程间(pshared 0线程,1进程)
互斥锁: 线程间(mutex)
互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性,排它性,独占性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。
注意:
(1)互斥锁用于线程的互斥,信号量用于线程的同步。
(2)互斥锁值只能为申请/释放锁,信号量值可以为非负整数 >= 0。
(3)互斥锁的加锁(申请锁)和解锁(释放锁)必须由同一个线程内部分别加锁、解锁,不可跨越别的线程;信号量可以由一个线程释放,另一个线程得到,是有先后顺序的。
(五)互斥锁函数
man 函数出现 No manual entry for pthread_mutex_xxx解决办法 :
sudo apt-get install manpages-posix-dev
头文件
#include <pthread.h> gcc .... -lptread
(1)初始化互斥锁(初始化互斥锁对象mutex)
int pthread_mutex_init(pthread_mutex_t * mutex, const pthread_mutexattr_t * attr);
(2)申请锁(如果无法获得锁,任务阻塞)
int pthread_mutex_lock(pthread_mutex_t * mutex);
(3)释放锁(执行完临界区要及时释放锁)
int pthread_mutex_unlock(pthread_mutex_t * mutex);
1)返回值:
成功 0
失败 返回错误码errno
2)mutex:操作的互斥锁对象
attr:互斥锁属性,NULL表示默认属性
(六)死锁
(1)死锁概念
死锁是指多个进程在运行过程中,因争夺资源而造成的一种僵局。当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。
(2)产生死锁的原因
a)资源竞争
竞争临时资源(临时资源包括硬件中断、信号、消息、缓冲区内的消息等),通常消息通信顺序进行不当,则会产生死锁
b)进程间推进顺序非法
两个进程互锁
(3)死锁产生的4个必要条件
(1)互斥条件:(独占性)进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一个进程所占用。
(2)请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放。
(3)不剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。
(4)环路等待条件:在发生死锁时,必然存在一个进程–资源的环形链。
(4)预防死锁:
资源一次性分配:一次性分配所有资源,这样就不会再有请求了;(破坏请求条件)
只要有一个资源得不到分配,也不给这个进程分配其他的资源;(破坏请保持条件)
可剥夺资源:即当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源;(破坏不可剥夺条件)
资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反。(破坏环路等待条件)
互斥程序
互斥锁程序:向1.txt文件中利用两个线程以每次写入一个字符的方式,写入字符串。
也可把上锁和解锁的四条语句注释掉,看一下加锁前后的结果对比。
1 #include <stdio.h>
2 #include <pthread.h>
3 #include <unistd.h>
4 #include <string.h>
5
6 #define NUM 20
7
8 FILE * fp;
9 pthread_mutex_t mutex;
10
11 void * write_pthread1(void * arg)
12 {
13 int ret, i;
14 char buf[NUM] = "hello world";
15
16 while(1)
17 {
18 pthread_mutex_lock(&mutex); //上锁
19 for(i = 0; i < strlen(buf); i++)
20 {
21 ret = fwrite(&buf[i], 1, 1, fp);
22 //printf("%c, %d\n", buf[i], ret);
23 if(ret == 0)
24 {
25 break;
26 }
27 fflush(fp);
28 usleep(10000);
29 }
30 fputc('\n', fp);
31 pthread_mutex_unlock(&mutex); //解锁
32 sleep(1);
33 }
34 }
35
36 void * write_pthread2(void * arg)
37 {
38 int ret, i;
39 char buf[NUM] = "welcome to China";
40
41 while(1)
42 {
43 pthread_mutex_lock(&mutex); //上锁
44 for(i = 0; i < strlen(buf); i++)
45 {
46 ret = fwrite(&buf[i], 1, 1, fp);
47 //printf("%c, %d\n", buf[i], ret);
48 if(ret == 0)
49 {
50 break;
51 }
52 fflush(fp);
53 usleep(10000);
54 }
55 fputc('\n', fp);
56 pthread_mutex_unlock(&mutex); //解锁
57 sleep(1);
58 }
59 }
60
61 int main(int argc, const char *argv[])
62 {
63 pthread_t wr_tid1;
64 pthread_t wr_tid2;
65
66 fp = fopen("1.txt", "w+");
67 if(fp == NULL)
68 {
69 perror("fopen fail");
70 return -1;
71 }
72
73 pthread_mutex_init(&mutex, NULL);
74
75 pthread_create(&wr_tid1, NULL, write_pthread1, NULL);
76 pthread_create(&wr_tid2, NULL, write_pthread2, NULL);
77
78 while(1)
79 {
80 sleep(1);
81 }
82 return 0;
83 }