线程同步

来源:互联网 发布:小型网络机柜 编辑:程序博客网 时间:2024/06/06 09:38

<span style="background-color: rgb(255, 255, 255); font-family: Arial, Helvetica, sans-serif;">当多个控制线程共享相同的内存时,需要确保每个线程看到一致的数据视图。如果对于某个变量来说,线程对它没有任何操作,或者只读,或者只有单个特定的线程访问,不会出现一致性的问题。但是当多个线程都能对某个变量进行修改的时候,就可能出现一致性问题。而线程同步就是要解决一致性问题,也就是让所有线程访问某个变量时,对它的任何操作都能获得期待的效果。</span>

举个例子,假设有A、B两个线程,如果B线程正准备读取某个变量X的值,但是这时恰好A线程对这个变量进行写操作,改变了该变量的值,然后B线程再读到这个值,可能就是一个期待之外的值了。

又比如说,假设有A、B两个线程,都要对某个变量X进行X++操作,但是完成该操作需要几个存储器周期:先从内存单元读入寄存器,然后在寄存器中对变量做增量操作,最后把心的值写回内存单元。大概需要这么三步,我们期待的结果是A,B两个线程对该变量进行加1操作之后,该变量的值比以前增加2。这是期待的结果,但是,加入A线程执行到第二步,也就是在寄存器中对变量做增量操作时,B线程开始从内存单元读取变量值,这时候,最终的结果将会是加1,这与我们期待的值并不一样。


一致性问题出现主要是因为多个线程之间的执行顺序完全是随机的,所以线程同步的关键问题就是如何让多个随机运行的线程在访问某个变量的时候变得有序,从而达到我们期望的效果。


我们经常用到的同步方法主要有以下几种:

1.互斥量

互斥量就是一把锁,当要对某个共享的数据进行访问时,先锁住它,处理完之后再解锁,在锁住的过程中,其他的线程无法访问。所以这样就确保了同一时间只有一个线程访问数据。

在使用互斥量的时候,往往是对某个共享的数据分配一个互斥量,然后哪个线程需要访问该数据,就对其进行加锁,处理结束之后,解锁。如果加锁的时候该数据已经被其他线程加锁,那么该线程就阻塞,直到占据该数据的线程解锁为止。

考虑两种情况,某线程对某个数据加锁,紧接着有加锁一次,这时该线程会被锁死。第二种情况,线程A对变量X加锁,线程B对变量Y加锁,此时A要对Y加锁,B要对X加锁,同样锁死。在应用多线程编程时要千万注意锁死的情况。


2.读写锁

读写锁跟互斥量不同的地方在于,读写锁有两个加锁的状态,相当于有两把锁,读锁和写锁,可以多个线程同时对某个变量加读锁,但是同一时刻只有一个线程能对某个变量加写锁。因为读数据并不改变该数据,而写数据则不是这样的。因此,读写锁非常适合于对数据结构读的次数远大于写的次数的情况。


3.条件变量

条件变量是另一种线程同步机制。当一个动作需要另外一个动作完成时才能进行,那么就可以使用条件变量。举个例子来说,假设一个程序有两个线程A和B,有一个工作队列msg,线程A负责在工作队列里添加新的工作项,而线程B负责处理工作队列的工作项。那么线程B只有在线程A往msg中添加了工作项之后,才能继续工作,也就是说,B的执行需要msg状态的改变,只有这个条件满足时,才能进行下面的处理,这就可以用条件变量。

当然,这个问题也能用轮询的方式解决,B线程定期的查询msg状态,当状态改变时,开始工作,但这会耗费大量cpu资源,当然也可以让查询的周期变长,这样实时性得不到满足。而用条件变量就能比较好的解决这个问题。

struct msg {struct msg *m_next;/* ... more stuff here ...*/};struct mst *workq;pthread_cond_t qready = PTHREAD_COND_INITIALIZER;pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER;void process_msg(void){struct msg *mp;for(;;) {pthread_mutex_lock(&qlock);while (workq == NULL)pthread_cond_wait(&&qready,&qlock);mp = workq;workq = mp->m_next;pthread_mutex_unlock(&qlock);}}void enqueue_msg(struct msg *mp){pthread_mutex_lock(&qlock);mp->m_next = workq;workq = mp;pthread_mutex_unlock(&qlock);pthread_cond_signal(&qready);}

条件变量本身是由互斥量保护的,线程再改变条件变量之前必须首先锁住互斥量。在上述程序中,条件是工作队列的状态,把消息放到工作队列时,需要占有互斥量,
pthread_cond_wait

该函数等待条件发生,pthread_cond_signal函数给线程发送条件发生的消息。


4.自旋锁

自旋锁和互斥量非常相似,区别在于,某个线程再试图对某个以加锁的资源进行加锁操作的时候,会通过休眠使线程阻塞,而自旋锁在这种情况下回保持自旋状态,也就是一直在那里保持循环,知道该资源能被加锁。因此自旋锁适用于锁被持有的时间比较短的情况。


总结:多线程编程的难点在于线程的同步。线程同步的原理其实都大同小异,关键在于我们是否能考虑到同步的问题,以及如何合理的设计程序的流程。

0 0
原创粉丝点击