UNIX高级环境编程笔记线程间同步

来源:互联网 发布:alexa mini软件下载 编辑:程序博客网 时间:2024/05/17 08:58

在多线程操作共享变量时以为以下的多种原因出现数据的不一致性

1)读写相同变量是的操作跨越了时钟周期或者该操作为非原子操作

2)存在操作顺序的不一致性

顺序的不一致性:

SLamport给的严格定义是:
“… the result of any execution is the same as if the operations of all the processors were executed in some sequential order, and the operations of each individual processor appear in this sequence in the order specified by its program.

这个概念初次理解起来拗口,不过不要紧,下面我会给出个很直观的例子帮助理解。

假设我们有两个线程(线程1和线程2)分别运行在两个CPU上,有两个初始值为0的全局共享变量xy,两个线程分别执行下面两条指令:

初始条件: x = y = 0;

线程 1

线程 2

x = 1;

y=1;

r1 = y;

r2 = x;

因为多线程程序是交错执行的,所以程序可能有如下几种执行顺序:

Execution 1

Execution 2

Execution 3

x = 1;

r1 = y;

y = 1;

r2 = x;

结果:r1==0 and r2 == 1

y = 1;

r2 = x;

x = 1;

r1 = y;

结果: r1 == 1 and r2 == 0

x = 1;

y = 1;

r1 = y;

r2 = x;

结果: r1 == 1 and r2 == 1

当然上面三种情况并没包括所有可能的执行顺序,但是它们已经包括所有可能出现的结果了,所以我们只举上面三个例子。我们注意到这个程序只可能出现上面三种结果,但是不可能出现r1==0 and r2==0的情况。

其实就是规定了两件事情:
1)每个线程内部的指令都是按照程序规定的顺序(program order)执行的(单个线程的视角)
2)线程执行的交错顺序可以是任意的,但是所有线程所看见的整个程序的总体执行顺序都是一样的(整个程序的视角)

第一点很容易理解,就是说线程1里面的两条语句一定在该线程中一定是x=1先执行,r1=y后执行。第二点就是说线程1和线程2所看见的整个程序的执行顺序都是一样的,举例子就是假设线程1看见整个程序的执行顺序是我们上面例子中的Execution 1,那么线程2看见的整个程序的执行顺序也是Execution 1,不能是Execution 2或者Execution 3

有一个更形象点的例子。伸出你的双手,掌心面向你,两个手分别代表两个线程,从食指到小拇指的四根手指头分别代表每个线程要依次执行的四条指令。SC的意思就是说:
1)对每个手来说,它的四条指令的执行顺序必须是从食指执行到小拇指
2)你两个手的八条指令(八根手指头)可以在满足(1)的条件下任意交错执行(例如可以是左1,左2,右1,右2,右3,左3,左4,右4,也可以是左1,左2,左3,左4,右1,右2,右3,右4,也可以是右1,右2,右3,左1,左2,右4,左3,左4等等等等)

其实说简单点,SC就是我们最容易理解的那个多线程程序执行顺序的模型。

3)使用变量的方式

  如对某个变量加1,然后基于这个数值做出某种决定。增量和做出决定这两个操作并非原子操作。

进程间同步的三种基本方式:

互斥量,读写锁以及条件变量。

互斥量:

Pthread_mutex_t:在使用互斥量之前需要对互斥量进行初始化。初始化分为动态和静态初始化。动态初始话调用pthread_mutex_init()函数同时结束时需要调用pthread_mutex_destory对互斥量进行销毁。静态初始化是通过赋值为PTHREAD_MUTEX_INITIALIZER进行的。

通过加锁互斥量进行加锁实现线程间的同步。Pthread_mutex_lock()对互斥量进行加锁pthread_mutex_unlock()进行解锁。

对互斥量进行加锁时应避免死锁的情况。Mutex A,B.线程1lockA,lock(B),unlcok(B),unlock(A)和线程2lock(B),lockA,unlock(A),unlcok(B)。线程21将有可能出现死锁的情况。

解决方案:

以相同顺序对互斥量进行加锁,或使用pthread_mutex_trylock()Trylock函数如果成功加锁就可能继续前进如果没有就将释放先前占有的锁,做好清理工作然后过一段时间后重新尝试。

读写锁:

非常适应于读多于写的环境。读写锁即可以多次对进行加锁,对读与读之间不进行互斥但是对于读写和写写之间是互斥的。即再加读锁成功的情况下可以加读锁。但是加写锁后就不能再进行加锁包括读锁和写锁。

条件变量:

条件变量给多进程提供了一个汇合的场合。条件变量和互斥量一起使用时,允许线程以无竞争的方式等待特定的条件发生。

条件变量由互斥量保护。线程在改变条件状态前必须先锁住互斥变量,其他线程在获得互斥量之前之前不会察觉到这种改变,因为必须先锁定互斥量才能计算条件。也就是所:

1)pthread_mutex_lock(A)

2)Pthread_cond_wait(B,A)

A互斥量用来保护条件变量B

Pthread_cond_wait();将调用的线程放到等待条件的线程链表上,然后对A进行解锁。同时这个线程进入休眠状态等待其他线程调用pthread_cond_signal()pthread_cond_broadcoast()发送信号唤醒该线程。在条件满足从而离开pthread_cond_wait()之前,mutex将被重新加锁,以与进入pthread_cond_wait()前的加锁动作对应

 

 

 

 

0 0