热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

linux多线程编程--使用条件变量的简单程序

问题描述:初始状态i3,j7;线程1同时做i++和j--的操作,当ij时,线程2开始工作,线程2工作完成后,线程1完成剩下的操作,使i7,j3.先贴最终版的代码如下:#in

问题描述:

初始状态i=3,j=7;线程1同时做i++和j--的操作,当i==j时,线程2开始工作,线程2工作完成后,线程1完成剩下的操作,使i=7,j=3.

先贴最终版的代码如下:

#include 
#include

using namespace std;

/* 当声明条件变量时,要记住条件变量与谓词是“链接”在一起的,建议将一组
* 不变量、谓词和它们的互斥量,以及一到多个条件变量封装为一个数据结构的
* 元素,并仔细记录下它们的关系,以免混淆 */
typedef struct th_struct {
int i, j; // i==j是谓词,与cond对应
bool is_th2_done; // 该谓词与cond_2对应
pthread_mutex_t lock; // 两个条件变量共用一个互斥量lock
pthread_cond_t cond;
pthread_cond_t cond_2;
}glob_data;

glob_data data; // 共享数据及互斥量、条件变量声明为全局量

void* changeVar(void*);
void* doSomething(void*);

int main() {
data.i = 3; data.j = 7; data.is_th2_dOne= false;
pthread_mutex_init(&data.lock, NULL);
pthread_cond_init(&data.cond, NULL); // 初始化条件变量
pthread_cond_init(&data.cond_2, NULL);

pthread_t thread1;
/* 线程创建后,立即进入“就绪”状态,只要相应条件一满足,就从
* changeVar入口函数开始执行 */
pthread_create(&thread1, NULL, changeVar, NULL);

pthread_t thread2;
pthread_create(&thread2, NULL, doSomething, NULL);

/*join函数等待相应线程执行,线程未执行完,调用join函数的主线程
则处于阻塞状态,直到线程执行完为止,然后回收资源空间,
* 此后线程ID不可用*/
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);

pthread_cond_destroy(&data.cond); // 动态初始化的,必须destroy
pthread_cond_destroy(&data.cond_2);
pthread_mutex_destroy(&data.lock);
return 0;
}

void* changeVar(void*) {
pthread_mutex_lock(&data.lock);
while (data.i<8) {
cout <<"thread1 run here!" < cout <<"now i= " < /* 通知等待条件变量的线程,如果有线程等待该条件变量,则“叫醒”等待的线程
* 如果没有线程等待该条件变量,则什么也没发生 */
pthread_cond_signal(&data.cond);
// 不可对已解锁的锁解锁
pthread_mutex_unlock(&data.lock); // 最好先给信号,再释放锁

/* 开始等待thread2的完成,cond_wait函数将阻塞调用它的该线程,
* 直到有条件变量通知它共享数据的状态已经改变为止*/
while (!data.is_th2_done)
pthread_cond_wait(&data.cond_2, &data.lock);
}
++data.i;
--data.j;
}
pthread_exit(NULL);
/* exit调用后,线程中止,完成清理工作,此后线程中的数据已经“old”
* 不可访问,但线程ID还可用 */
}

void* doSomething(void*) {
/* 阻塞式加锁,如果加锁失败,阻塞该线程,并一直检测该锁的状态
* 当锁被解开后,该线程恢复,重新加锁,加锁成功则执行之后的代码*/
pthread_mutex_lock(&data.lock);
// 等待谓词i==j的到来,如果条件不满足,则线程一直被阻塞
while (data.i!=data.j) // 用while循环判断谓词是很有必要的
pthread_cond_wait(&data.cond, &data.lock);
if (data.i==data.j) {
cout < cout <<"thread2 start!" < cout <<"thread2 end!" < cout < data.is_th2_dOne= true;
// 通知线程1我已经完成
pthread_cond_signal(&data.cond_2);
}
pthread_mutex_unlock(&data.lock);
pthread_exit(NULL);
}
以上知识点来源于 《POSIX多线程程序设计》,这是本很好的书,将函数的原理讲得很透彻。

下面是我比较失败的版本,贴出来,然后分析:

#include 
#include

using namespace std;

int i(3), j(7);
pthread_mutex_t lock;
pthread_cond_t cond;

void* changeVar(void*);
void* doSomething(void*);

int main() {
pthread_mutex_init(&lock, NULL);
pthread_cond_init(&cond, NULL); // 初始化条件变量

pthread_t thread1;
pthread_create(&thread1, NULL, changeVar, NULL);

pthread_t thread2;
pthread_create(&thread2, NULL, doSomething, NULL);

pthread_join(thread1, NULL);
pthread_join(thread2, NULL);

pthread_cond_destroy(&cond);
pthread_mutex_destroy(&lock);
return 0;
}

void* changeVar(void*) {
pthread_mutex_lock(&lock);
cout < while (i<8) {
cout <<"thread1 running!" < cout <<"i=" < pthread_mutex_unlock(&lock); // 释放锁
pthread_cond_signal(&cond); // 通知等待条件变量的线程
sleep(0.1);
}
++i;
--j;
}
pthread_mutex_unlock(&lock);
pthread_exit(NULL);
}

void* doSomething(void*) {
pthread_mutex_lock(&lock);
if (i!=j) {
pthread_mutex_unlock(&lock);
pthread_cond_wait(&cond, &lock);
}
cout < cout <<"thread2 start!" < cout <<"thread2 end!" < pthread_mutex_unlock(&lock);
pthread_exit(NULL);
}


程序执行过程如下:

1 刚开始,两个线程争夺锁资源,如果线程1胜利,goto 2,否则goto 5;

2 执行到i==j==5时,当有如下输出的时候:

thread1 running!
i=3 j=7
thread1 running!
i=4 j=6
thread1 running!
i=5 j=5
go to thread2!
thread1通知条件变量并解锁,然后它自己sleep;

3 被阻塞的thread2此时发现锁被解开后,对其加锁,然后检测i与j是否相等,因为它们相等,所以开始输出;

4 如果thread1睡眠时间很短,短到thread2来不及加锁,就会得到下面的结果:

thread1 run here!
thread1 run here!
thread1 run here!
go to thread2!
thread1 run here!
thread1 run here!

thread2 start!
thread2 end!
如果thread1睡眠较短,以致它cout的时候thread2也在cout,它们就会争夺cout的资源,出现下面的结果:

thread1 run here!
now i= 3j= 7
thread1 run here!
now i= 4j= 6
thread1 run here!
now i= 5j= 5
go to thread2!

thread2 start!
thread2 end!
thread1 run here!
now i= 6j= 4
thread1 run here!
now i= 7j= 3
如果thread1睡眠较长,就会让thread2先cout,然后thread1在做剩下的事情,这样输出的就是正确的结果。

5 当thread2先占据了锁资源,也能输出正确的结果。分析完毕!


推荐阅读
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 深入理解Java虚拟机的并发编程与性能优化
    本文主要介绍了Java内存模型与线程的相关概念,探讨了并发编程在服务端应用中的重要性。同时,介绍了Java语言和虚拟机提供的工具,帮助开发人员处理并发方面的问题,提高程序的并发能力和性能优化。文章指出,充分利用计算机处理器的能力和协调线程之间的并发操作是提高服务端程序性能的关键。 ... [详细]
  • Linux 中使用 clone 函数来创建线程
    2019独角兽企业重金招聘Python工程师标准Linux上创建线程一般使用的是pthread库实际上libc也给我们提供了创建线程的函数那就是cloneintclone(i ... [详细]
  • 作者一直强调的一个概念叫做oneloopperthread,撇开多线程不谈,本篇博文将学习,怎么将传统的IO复用pollepoll封装到C++类中。1.IO复用复习使用p ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文介绍了为什么要使用多进程处理TCP服务端,多进程的好处包括可靠性高和处理大量数据时速度快。然而,多进程不能共享进程空间,因此有一些变量不能共享。文章还提供了使用多进程实现TCP服务端的代码,并对代码进行了详细注释。 ... [详细]
  • C++中的三角函数计算及其应用
    本文介绍了C++中的三角函数的计算方法和应用,包括计算余弦、正弦、正切值以及反三角函数求对应的弧度制角度的示例代码。代码中使用了C++的数学库和命名空间,通过赋值和输出语句实现了三角函数的计算和结果显示。通过学习本文,读者可以了解到C++中三角函数的基本用法和应用场景。 ... [详细]
  • Go语言实现堆排序的详细教程
    本文主要介绍了Go语言实现堆排序的详细教程,包括大根堆的定义和完全二叉树的概念。通过图解和算法描述,详细介绍了堆排序的实现过程。堆排序是一种效率很高的排序算法,时间复杂度为O(nlgn)。阅读本文大约需要15分钟。 ... [详细]
  • 本文介绍了Codeforces Round #321 (Div. 2)比赛中的问题Kefa and Dishes,通过状压和spfa算法解决了这个问题。给定一个有向图,求在不超过m步的情况下,能获得的最大权值和。点不能重复走。文章详细介绍了问题的题意、解题思路和代码实现。 ... [详细]
  • 深入解析Linux下的I/O多路转接epoll技术
    本文深入解析了Linux下的I/O多路转接epoll技术,介绍了select和poll函数的问题,以及epoll函数的设计和优点。同时讲解了epoll函数的使用方法,包括epoll_create和epoll_ctl两个系统调用。 ... [详细]
  • 第七课主要内容:多进程多线程FIFO,LIFO,优先队列线程局部变量进程与线程的选择线程池异步IO概念及twisted案例股票数据抓取 ... [详细]
  • 线程漫谈——线程基础
    本系列意在记录Windwos线程的相关知识点,包括线程基础、线程调度、线程同步、TLS、线程池等。进程与线程理解线程是至关重要的,每个进程至少有一个线程,进程是线程的容器,线程才是真正的执行体,线程必 ... [详细]
  • Linux线程的同步和互斥
    目录1、线程的互斥2、可重入VS线程安全3、线程的同步1、线程的互斥 ... [详细]
  • 主线:设计窗口类注册窗口类产生窗口显示窗口更新窗口消息循环(将消息路由到窗口中去处理)。APPMODUL.CPP源文件被编译链接进入项目,从APPMOD ... [详细]
author-avatar
政庆雅竹8
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有