通用线程:POSIX 线程详解(3)

时光成就了岁月

时光成就了岁月

2016-02-19 15:55

今天图老师小编要向大家分享个通用线程:POSIX 线程详解(3)教程,过程简单易学,相信聪明的你一定能轻松get!

  第 3 部分
  
  内容:
  
  条件变量
  pthread_cond_wait() 小测验
  初始化和清除
  等待
  发送信号和广播
  工作组
  队列
  data_control 代码
  调试时间
  工作组代码
  代码初排
  有关清除的注重事项
  创建工作
  threadfunc()
  join_threads()
  结束语
  参考资料
  关于作者
  
  
  相关内容:
  
  第 2 部分:称作互斥对象的小玩意
  第 1 部分:POSIX 线程详解
  
  使用条件变量提高效率
  作者:Daniel Robbins
  
  
  本文是 POSIX 线程三部曲系列的最后一部分,Daniel 将具体讨论如何使用条件变量。条件变量是 POSIX 线程结构,可以让您在碰到某些条件时“唤醒”线程。可以将它们看作是一种线程安全的信号发送。Daniel 使用目前您所学到的知识实现了一个多线程工作组应用程序,本文将围绕着这一示例而进行讨论。
  
  条件变量详解
  在上一篇文章结束时,我描述了一个比较非凡的难题:假如线程正在等待某个特定条件发生,它应该如何处理这种情况?它可以重复对互斥对象锁定和解锁,每次都会检查共享数据结构,以查找某个值。但这是在浪费时间和资源,而且这种繁忙查询的效率非常低。解决这个问题的最佳方法是使用 pthread_cond_wait() 调用来等待非凡条件发生。
  
  了解 pthread_cond_wait() 的作用非常重要 -- 它是 POSIX 线程信号发送系统的核心,也是最难以理解的部分。
  
  首先,让我们考虑以下情况:线程为查看已链接列表而锁定了互斥对象,然而该列表恰巧是空的。这一特定线程什么也干不了 -- 其设计意图是从列表中除去节点,但是现在却没有节点。因此,它只能:
  
  锁定互斥对象时,线程将调用 pthread_cond_wait(&mycond,&mymutex)。pthread_cond_wait() 调用相当复杂,因此我们每次只执行它的一个操作。
  
  pthread_cond_wait() 所做的第一件事就是同时对互斥对象解锁(于是其它线程可以修改已链接列表),并等待条件 mycond 发生(这样当 pthread_cond_wait() 接收到另一个线程的“信号”时,它将清醒)。现在互斥对象已被解锁,其它线程可以访问和修改已链接列表,可能还会添加项。
  
  此时,pthread_cond_wait() 调用还未返回。对互斥对象解锁会立即发生,但等待条件 mycond 通常是一个阻塞操作,这意味着线程将睡眠,在它清醒之前不会消耗 CPU 周期。这正是我们期待发生的情况。线程将一直睡眠,直到特定条件发生,在这期间不会发生任何浪费 CPU 时间的繁忙查询。从线程的角度来看,它只是在等待 pthread_cond_wait() 调用返回。
  
  现在继续说明,假设另一个线程(称作“2 号线程”)锁定了 mymutex 并对已链接列表添加了一项。在对互斥对象解锁之后,2 号线程会立即调用函数 pthread_cond_broadcast(&mycond)。此操作之后,2 号线程将使所有等待 mycond 条件变量的线程立即清醒。这意味着第一个线程(仍处于 pthread_cond_wait() 调用中)现在将清醒。
  
  现在,看一下第一个线程发生了什么。您可能会认为在 2 号线程调用 pthread_cond_broadcast(&mymutex) 之后,1 号线程的 pthread_cond_wait() 会立即返回。不是那样!实际上,pthread_cond_wait() 将执行最后一个操作:重新锁定 mymutex。一旦 pthread_cond_wait() 锁定了互斥对象,那么它将返回并答应 1 号线程继续执行。那时,它可以马上检查列表,查看它所感爱好的更改。
  
  停止并回顾!
  那个过程非常复杂,因此让我们先往返顾一下。第一个线程首先调用:
  
  
  
  pthread_mutex_lock(&mymutex);
  
  
  
  
  然后,它检查了列表。没有找到感爱好的东西,于是它调用:
  
  
  
  pthread_cond_wait(&mycond, &mymutex);
  
  
  
  
  
  然后,pthread_cond_wait() 调用在返回前执行许多操作:
  
  
  
  pthread_mutex_unlock(&mymutex);
  
  
  
  
  它对 mymutex 解锁,然后进入睡眠状态,等待 mycond 以接收 POSIX 线程“信号”。一旦接收到“信号”(加引号是因为我们并不是在讨论传统的 UNIX 信号,而是来自 pthread_cond_signal() 或 pthread_cond_broadcast() 调用的信号),它就会清醒。但 pthread_cond_wait() 没有立即返回 -- 它还要做一件事:重新锁定 mutex:
  
  
  
  pthread_mutex_lock(&mymutex);
  
  
  
  
  pthread_cond_wait() 知道我们在查找 mymutex “背后”的变化,因此它继续操作,为我们锁定互斥对象,然后才返回。
  
  pthread_cond_wait() 小测验
  现在已回顾了 pthread_cond_wait() 调用,您应该了解了它的工作方式。应该能够叙述 pthread_cond_wait() 依次执行的所有操作。尝试一下。假如理解了 pthread_cond_wait(),其余部分就相当轻易,因此请重新阅读以上部分,直到记住为止。好,读完之后,能否告诉我在调用 pthread_cond_wait() 之前,互斥对象必须处于什么状态?pthread_cond_wait() 调用返回之后,互斥对象处于什么状态?这两个问题的答案都是“锁定”。既然已经完全理解了 pthread_cond_wait() 调用,现在来继续研究更简单的东西 -- 初始化和真正的发送信号和广播进程。到那时,我们将会对包含了多线程工作队列的 C 代码了如指掌。
  
  初始化和清除
  条件变量是一个需要初始化的真实数据结构。以下就初始化的方法。首先,定义或分配一个条件变量,如下所示:
  
  
  
  
  pthread_cond_t mycond;
  
  
  
  
  然后,调用以下函数进行初始化:
  
  
  
  
  pthread_cond_init(&mycond,NULL);
  
  
  
  
  瞧,初始化完成了!在释放或废弃条件变量之前,需要毁坏它,如下所示:
  
  
  
  
  pthread_cond_destroy(&mycond);
  
  
  
  
  很简单吧。接着讨论 pthread_cond_wait() 调用。
  
  等待
  一旦初始化了互斥对象和条件变量,就可以等待某个条件,如下所示:
  
  
  
  
  pthread_cond_wait(&mycond, &mymutex);
  
  
  
  
  请注重,代码在逻辑上应该包含 mycond 和 mymutex。一个特定条件只能有一个互斥对象,而且条件变量应该表示互斥数据“内部”的一种非凡的条件更改。一个互斥对象可以用许多条件变量(例如,cond_empty、cond_full、cond_cleanup),但每个条件变量只能有一个互斥对象。
  
  发送信号和广播
  对于发送信号和广播,需要注重一点。假如线程更改某些共享数据,而且它想要唤醒所有正在等待的线程,则应使用 pthread_cond_broadcast 调用,如下所示:
  
  
  
  
  pthread_cond_broadcast(&mycond);
  
  
  
  
  在某些情况下,活动线程只需要唤醒第一个正在睡眠的线程。假设您只对队列添加了一个工作作业。那么只需要唤醒一个工作程序线程(再唤醒其它线程是不礼貌的!):
  
  
  
  
  pthread_cond_signal(&mycond);
  
  
  
  
  此函数只唤醒一个线程。假如 POSIX 线程标准答应指定一个整数,可以让您唤醒一定数量的正在睡眠的线程,那就更完美了。但是很可惜,我没有被邀请参加会议。
  
  工作组
  我将演示如何创建多线程工作组。在这个方案中,我们创建了许多工作程序线程。每个线程都会检查 wq(“工作队列”),查看是否有需要完成的工作。假如有需要完成的工作,那么线程将从队列中除去一个节点,执行这些特定工作,然后等待新的工作到达。
  
  与此同时,主线程负责创建这些工作程序线程、将工作添加到队列,然后在它退出时收集所有工作程序线程。您将会碰到许多 C 代码,好好预备吧!
  
  队列
  需要队列是出于两个原因。首先,需要队列来保存工作作业。还需要可用于跟踪已终止线程的数据结构。还记得前几篇文章(请参阅本文结尾处的参考资料)中,我曾提到过需要使用带有特定进程标识的 pthread_join 吗?使用“清除队列”(称作 "cq")可以解决无法等待任何已终止线程的问题(稍后将具体讨论这个问题)。以下是标准队列代码。将此代码保存到文件 queue.h 和 queue.c:
  
  queue.h
  /* queue.h
  ** Copyright 2000 Daniel Robbins, Gentoo Technologies, Inc.
  
   ** Author: Daniel Robbins
  ** Date: 16 Jun 2000
  */
  
  typedef strUCt node {
  struct node *next;
  } node;
  
展开更多 50%)
分享

猜你喜欢

通用线程:POSIX 线程详解(3)

编程语言 网络编程
通用线程:POSIX 线程详解(3)

通用线程:POSIX 线程详解(2)

编程语言 网络编程
通用线程:POSIX 线程详解(2)

s8lol主宰符文怎么配

英雄联盟 网络游戏
s8lol主宰符文怎么配

POSIX 线程详解(1)

编程语言 网络编程
POSIX 线程详解(1)

Java多线程的用法详解

编程语言 网络编程
Java多线程的用法详解

lol偷钱流符文搭配推荐

英雄联盟 网络游戏
lol偷钱流符文搭配推荐

基于Java回顾之多线程详解

编程语言 网络编程
基于Java回顾之多线程详解

深入java线程池的使用详解

编程语言 网络编程
深入java线程池的使用详解

lolAD刺客新符文搭配推荐

英雄联盟
lolAD刺客新符文搭配推荐

山寨 现在的手机好假 - QQ图案分组

山寨 现在的手机好假 - QQ图案分组

数据库程序的精简安装

数据库程序的精简安装
下拉加载更多内容 ↓