线程和进程同步及进程间通信总结

一、线程(或进程)同步的理解

互斥:两个线程(进程)不能同时访问同一资源,访问资源的顺序不确定

同步:两个线程(进程)具有一定的先后顺序来访问同一资源。(互斥的同时,线程(进程)需按照先后顺序访问资源。)

比如:生产者和消费者,必须是现生产了才能消费;消费了才能有空地,继续生产。

二、线程同步机制

1、互斥锁

1)如果一个线程加了锁,正在访问共享资源,失去了cpu,此时另一个线程没加锁,也是能访问共享资源的。这样就造成了混乱。

解决方法:访问共享资源的线程都要加锁。

2)pthread_trylock是非阻塞的,若是锁被占用,则返回。

pthread_lock 则是阻塞的,若是锁被占用,则阻塞。

3)死锁

对共享资源锁住两次:第二次锁的时候,因为上一次锁没有释放,所以,第二次锁的时候被阻塞住了。

4)使用 pthread_init()初始化的锁,需要使用 pthread_destroy()销毁锁。

2、读写锁//适用于读操作线程明显多于写操作线程的情况。

十二字诀:

写独占,读共享

写锁优先级高。

假定当前是写锁,此时,有锁来抢,是抢不到锁的(写独占);

假定当前是读锁,此时,只有读锁来抢,是能进入共享区的(读共享);

假定当前是读锁1,此时,有读锁和写锁都来抢,则都无法进入共享区,待读锁1解锁后,写锁会抢到锁(写锁优先级高);

读写锁的优势:都是读的时候,可以共享,提高效率;既有读又有写的时候使用时,有优势。

3、条件变量难点)//一个生产者对应多个消费者

优点:当多个消费者调用pthread_cond_wait()时候,发生阻塞,此时消费者线程会释放CPU的,避免了不必要的竞争。

与互斥锁配合使用(从如下的pthread_cond_wait参数也可以看出来,是需要互斥锁的。);也会造成线程阻塞。

#include <pthread.h>

int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);

a.阻塞等待一个条件变量cond满足条件,即:满足条件执行,不满足阻塞。

b.释放已经掌握的互斥锁,即相当于phread_mutex_unlock(&mutex);a,b步骤为一个原子操作,即不能分开,同时完成。(所以,需调用此函数前,需要对mutex进行初始化和加锁,对cond进行初始化)

c.被唤醒,pthread_cond_wait函数返回前,解除阻塞,并重新申请获取互斥锁pthread_mutex_lock(&mutex);//因为接下来要对共享数据操作。

(因为b中解锁了嘛,所以此处需要重新拿锁)

使用phtread_cond_signal函数或者phtread_cond_broadcast来唤醒:

phtread_cond_signal函数能够唤醒阻塞在条件变量上至少一个线程。

phtread_cond_broadcast函数能够唤醒所有阻塞在条件变量上的线程。

小结:

1)为什么需要互斥锁?而且是在条件变量使用前加锁?

因为,是多线程对共享数据的操作。

因为,条件变量while循环中判断的时候也是对共享区数据的判断。

2)为什么需要条件变量?

因为,只有生产了,才能去消费。即:只有某个条件成立,线程才会执行。否则的话,线程需要不断的去判断条件是否成立。

3)pthread_cond_wait函数原子操作中,为什么要解锁?

因为,条件变量不成立的话,是阻塞的,如果一直阻塞在这里,就一直没有解锁,一直没有解锁的话,生产者就无法获取锁,无法生产。

另一个消费者线程也能去消费。

4)为什么pthread_cond_wait()的是条件不成立时,才执行?

因为,条件成立的话,消费者就直接消费了。只有条件不成立,才阻塞在这里,等待唤醒。

5)为什么pthread_cond_wait()的判断条件是while,而不是if?

因为,倘若只有一个消费者线程:两者效果是相同的

假如是if的话,if条件成立,phtread_cond_wait函数执行,则 解锁,阻塞。。。。

当生产者线程生产后,条件满足,pthread_cond_wait函数收到信号,加锁,顺序向下执行。

而使用while函数的话,while条件成立,phtread_cond_wait函数执行,则 解锁,阻塞。。。。

当生产者线程生产后,条件满足,pthread_cond_wait函数收到信号,加锁,while会再次判断,此时while条件不满足,从而退出循环。

但倘若有多个消费者线程,只有一个线程收到signal后,会加锁成功,然后消费,解锁,此时其他的消费者线程抢到锁了,如果是if,直接执行的话,此时是没有商品的,造成错误,故需要使用while再次判断。

6)pthread_cond_wait()函数被唤醒后为什么自动加锁?

因为,唤醒后,就需要再次操作共享区数据了,(while中判断是第一次),所以要加锁,此时,生产者线程被阻塞在锁那。

7)生产者线程中pthread_cond_signal()函数的位置?//有的地方说也可以先发信号,后解锁

操作完共享区域后,立马解锁,解锁后,发信号。

pthread_mutex_lock();
//do sth.
pthread_mutex_unlock();
pthread_cond_signal();

8) 生产者线程中pthread_cond_signal()函数执行时,如果没有阻塞的消费者线程怎么办?

无所谓,生产者该发信号,发信号,只不过是消费者线程没有接收的,消费者有东西消费,并未阻塞。

9)如果两个线程操作共享区内的数据没有先后顺序,则直接用互斥锁就可以;如果有先后顺序,则需要用到条件变量。

4、信号量(也能用于进程同步、同时也是进程间通信间的机制)(难点!!!!)

也就是说,如果是进程间共享资源互斥的话,需要使用信号量,如下所示:

sem_init(&blank_number,1,1);//第三个参数为1,与线程的互斥锁一样了。

互斥量的值是1,,信号量的值是N,

即:可以同时允许N个线程同时访问共享资源。信号量的初值决定了占用信号量的线程个数。当然,一个进程所能创建的线程也是有上限的。

当只有一个信号量,且初值为1,此时与互斥量相似,表示的是线程(进程)间的互斥;

当有多个信号量,一个线程控制另一个线程信号量的资源释放,这样就是线程的同步。(通俗的理解为,这个线程准备好了,告知另一个线程。)

信号量的理解方式一:可以理解信号量的值必须大于0,表示当前剩余资源的数量。减到0就会阻塞。

信号量理解方式二:参考该文。

小结:互斥量、读写锁、条件变量都只能在线程中使用(从函数名中可以看出来,pthread...)。

注意:进程间使用信号量同步的时候,信号量是放在共享内存中的。(同一进程中可以通过全局变量操作,不同进程是通过共享内存来操作的)

三、进程通信机制

六种方式:

1、管道;

2、消息队列

3、信号

4、信号量//System V 和posix两种形式

5、共享内存//System V 和posix两种形式

6、套接字

四、进程同步机制

1、信号量//同第二章

2、phread_mutex中的修改属性。

属性修改方法如下:

示例:

 

 上例中,父子进程操作共享的区域。

如果是没有血缘关系的进程,也是相同的道理,需要结合共享内存来使用!!!!

 3、文件锁//多个进程对同一文件进行操作。(与线程间的读写锁一样,写独占,读共享)

回顾:

 

 

 之前,使用f contrl函数配合 f getflag  和 f  setflag  宏来 修改文件属性,比如,读写,可追加(append),可覆盖,阻塞或非阻塞。

当前:文件锁

 其中,结构体中的pid_t  只有cmd 为 F_GETLK时才会使用。

 F_SETLOCK   是非阻塞的,相当于trylock;

F_SETLKW  是阻塞的,相当于wait(常用)

与lock和unlock不同的是,文件锁,加锁和解锁都用F_SETLKW(阻塞的函数,常用)来实现(通过修改结构体struct flock属性来区分)

对于fcntl函数,如果参数cmd是  如下三个:F_SETLK  F_SETLKW F_GETLK

则变参是传 struct  flolck结果体,如下所示:

 示例:

 

 

 

猜你喜欢

转载自blog.csdn.net/modi000/article/details/124864068