同步与互斥

同步与互斥

简介
1. 多线程共享一个进程的地址空间,多线程间通信容易进行,但是多线程同时访问共享对象时需要引入同步和互斥机制.
2. 同一个进程的线程共享进程内的绝大部分资源,当一段访问这些共享资源的代码块,有可能被多个线程执行,那么这块代码就被叫做临界区.
3. 当有多个线程并发的在临界区执行时,程序的执行结果会出现不确定性,这种情况被叫做静态条件.

谈到linux的并发,必然涉及到线程之间的同步和互斥,Linux主要为我们提供了几种实现线程间同步互斥的 机制,本文主要介绍互斥锁,条件变量和信号量。互斥锁和条件变量包含在pthread线程库中,使用时需要包含

            pthread_mutex_lock(&mutex); 
               while( a<b ) 
            pthread_cond_wait(&cond,&mutex); 
            pthread_mutex_unlock(&mutex); 

调用线程应该在它测试或调用pthread_cond_wait之前获得一个互斥量,以避免在测试条件的时候有其他 线程改写条件中变量的值。pthread_cond_wait函数中第二个参数是一个互斥量类型的指针,线程在调用
pthread_cond_wait后会隐式的原子地释放mutex互斥量并阻塞,允许其他线程获得互斥量并修改断言中
的变量。当线程成功的从pthread_cond_wait中返回时,它就再次拥有了互斥量,并且不用显式的重新获
得互斥量。
当其他线程修改了断言中变量的值后可以调用pthread_cond_signal函数来唤醒一个等待在某个断言成真 的线程。也可以使用pthread_cond_broadcast(pthread_cond_t*)函数来唤醒所有等待在某个条件变量 上的线程。在修改断言中出现的任一变量之前要获得互斥量。示例代码如下:

            pthread_mutex_lock(&mutex); 
            a++; 
            pthread_cond_signal(&cond);//pthread_cond_broadcast(&cond); 
            pthread_mutex_unlock(&mutex); 

3.信号量
信号量是一个整型变量,它带有两个原子操作wait和signal。wait操作还可以被称为down、P操作。signal操 作还可以被称为up、V、post操作。
如果S大于0,wait操作就在一个原子操作中对其进行减量运算。如果S等于0,wait操作就就在一个原子操作中 测试S,阻塞调用程序,将调用程序放入wait的等待队列中。
如果有线程在信号量上阻塞,则S必然等于0,signal操作就会解除对某一个等待线程的阻塞。如果S大于0,即 没有线程阻塞在信号量上,signal就对S进行增量操作。
可以把信号量理解为临界区中资源的可用数量,wait表示对资源的申请,当没有可用资源时信号量为0,signal 表示线程使用资源后,对资源的释放。

下面介绍几种通过信号量来控制线程按某种顺序执行的方法:
1.线程1中a先于线程2中b执行[S初始话为0]

 Process 1: 
            a; 
            signal(&S); 
 Process 2: 
            wait(&S); 
            b; 
2.线程1中a于线程2中b语句交替执行[S,Q初始化为1] 
  Process 1: 
            while(1){ 
            wait(&S); 
            a; 
            signal(&Q); 
            } 
  Process 2: 
            while(1){ 
            wait(&Q); 
            b; 
            signal(&S); 
            } 
可通过让S为0Q为1,或让S为1Q为0,来保证让a或b先执行。 
需要注意的是,为了避免申请多个资源发生死锁,应按照相同的顺序申请资源


linux中信号量有无名信号量和命名信号量之分,无名信号量可用于线程之间的同步和互斥,命名信号量可用 
于进程间的通信,命名信号量与命名管道相似,以文件的形式存储于磁盘上。 

无名信号量:
类型声明: sem_t sem;
初始化: int sem_init(sem_t* sem,int pshared,int value);
参数pshared为0,表示信号量只能由初始化这个信号量的进程中的线程使用。
value表示要将sem初始化的值。value不能为负。
操作:
int sem_post(sem_t* sem);//signal操作
int sem_wait(sem_t* sem);//wait操作
销毁:
int sem_destroy(sem_t* sem);
使用方法:
通常先由主线程调用sem_init对信号量进行初始化。然后在其他线程中调用post或wait函数。示例代码如下:
static sem_t sem;
sem_init(&sem,0,1);
sem_wait(&sem);
/critical section/
sem_post(&sem);

命名信号量:
类型声明: semt sem;
初始化:
sem_t* sem_open(const char* name,int oflag,…);
参数oflag用来确定是创建信号量,还是仅仅由函数对其访问。若oflag中的O_CREAT比特位被设置,则需 要另外两个参数,mode_t mode为文件权限,unsigned value为信号量值。
关闭: int sem_close(sem_t* sem);
删除: int sem_unlink(sem_t* sem);
操作方式与无名信号量相同。

猜你喜欢

转载自blog.csdn.net/fightHHA/article/details/81542805