多线程编程笔记

多线程的问题

在多线程应用中,子线程一定会修改某些公共资源(包括全局变量、磁盘文件等),这样很有可能会影响其它子线程。由于不同线程是并行执行的,有可能某个时刻某个线程对公共资源进行了部分修改,此时公共资源是无效的,CPU 切换到另一个线程执行并访问此公共资源,这样将有可能造成巨大灾难。

要解决此问题,就应该使需要相同公共资源的不同线程不能同时执行,所以多线程问题分析的基础是互斥。

线程同步和线程互斥

线程间的关系可以分为无关和相交两种。当多个线程无关的时候,其中某个线程的启动不会依赖于另一个线程,它们启动和完成的先后顺序也没有特别要求,所以不需要任何机制来控制和协调它们运行,这是最简单的情况;而相交线程之间则较为复杂,有可能某个线程的启动依赖于另一个线程的完成,也有可能某个线程的完成达成另一线程启动的条件,这时就需要一些机制来控制和协调它们运行。

根据线程执行的先后顺序要求的不同需要用到线程同步和线程互斥。如果某个线程需要用到另一线程的运行结果,那么此线程应该让其先启动并完成后才启动,这种情况下的两个线程执行的先后顺序是有严格要求的,乱序就会出问题,此时就应该使用线程同步;如果两个线程仅仅访问相同的公共资源,而不是一个线程的启动依赖于另一个线程的完成,这种情况下的两个线程执行的先后顺序没有严格要求,谁先谁后无所谓,此时就应该使用线程互斥。

线程同步是一种更为复杂的线程互斥(即: 线程执行的先后顺序有严格要求的互斥)。在考虑选择线程同步还是线程互斥的时候,思考一下线程执行的先后顺序是否有严格要求即可作出正确选择。

互斥体

互斥体的理解和使用都是很简单的,使用前加锁使用后解锁。互斥体的主要功能是锁,只有锁定和解锁两种状态,在处理某些问题的时候会显得吃力。例如,如果线程正在等待公共资源内某个条件出现,用互斥体该如何处理呢?代码可以锁定互斥体,然后检查条件是否出现,如果条件未出现,则解锁互斥体,睡眠一段时间,再次锁定互斥体并检查,直到条件出现再进行相应处理。如此锁定、检查再解锁的操作可能会重复很多次,这是互斥体天生能力的不足,正确的方法是使用条件变量。

条件变量

互斥体不能解决等待的问题,它仅仅解决锁的问题。如果用互斥体来解决上面问题,只能在线程中反复检查。而条件变量可以解决等待问题,在等待条件出现过程中不用耗费任何 CPU 周期。为了防止竞争,条件变量的使用总是和互斥体结合在一起。等待条件出现代码如下:

void WaitOnCondition()
{
  // 锁定互斥体
  pthread_mutex_lock(&mutex);

  // 如果条件已出现,则不用待等待
  // 如果条件未出现,则线程进入睡眠,直到条件出现
  while ( ready_to_go==false ) {
    pthread_cond_wait(&condition, &mutex);
  }

  // 处理

  // 重置条件
  ready_to_go = false;

  // 解锁互斥体
  pthread_mutex_unlock(&mutex);
}

条件出现代码如下:

void SignalCondition()
{
  // 锁定互斥体
  pthread_mutex_lock(&mutex);

  // 产生条件
  ready_to_go = true;

  // 发出条件产生信号
  pthread_cond_signal(&condition);

  // 解锁互斥体
  pthread_mutex_unlock(&mutex);
}

使用条件变量过程中最难理解的部分是 pthread_cond_wait 函数。等待函数会先解锁互斥体(其它线程才能锁定互斥体并产生条件),然后开始等待,当其它线程产生条件并发出信号时,等待函数并不会立即返回,它会先锁定互斥体再返回。可以看出互斥体在等待函数调用前和返回后都处于锁定状态。

posted on 2014-07-04 15:08  kvmisc  阅读(250)  评论(0)    收藏  举报

导航