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

linux线程的同步二(互斥锁和条件变量)

互斥锁和条件变量为了允许在线程或进程之间共享数据,同步时必须的,互斥锁和条件变量是同步的基本组成部分。1、互斥锁互斥锁是用来保护临界区资源,实际上保护的是临界区中被操纵的数据,互斥

  为了允许在线程或进程之间共享数据,同步时必须的,互斥锁和条件变量是同步的基本组成部分。

1、互斥锁

  互斥锁是用来保护临界区资源,实际上保护的是临界区中被操纵的数据,互斥锁通常用于保护由多个线程或多进程分享的共享数据。一般是一些可供线程间使用的全局变量,来达到线程同步的目的,即保证任何时刻只有一个线程或进程在执行其中的代码。一般加锁的轮廓如下:

pthread_mutex_lock()
临界区
pthread_mutex_unlock()

互斥锁API

pthread_mutex_lock(pthread_mutex_t *mutex);

 用此函数加锁时,如果mutex已经被锁住,当前尝试加锁的线程就会阻塞,直到互斥锁被其他线程释放。当此函数返回时,说明互斥锁已经被当前线程成功加锁.

pthread_mutex_trylock(pthread_mutex_t *mutex);

 用此函数加锁时,如果mutex已经卑琐主,当前尝试加锁的线程不会阻塞,而是立即返回,返回的错误码为EBUSY,而不是阻塞等待。

pthread_mutex_unlock(pthread_mutex_t *mutex);

注意使用锁之前要记得初始化。互斥锁的初始化有两种初始化方式:

1.对于静态分配的互斥锁一半用宏赋值的方式初始化

eg: static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

2.对于动态分配的互斥锁(如调用malloc)或分配在共享内存中,则必须调用pthread_mutex_init(pthread_mutex *mutex, pthread_mutexattr_t *mutexattr)函数来进行初始化。

例子1:写个程序实现生产者—消费者问题,先只考虑多个生产者线程之间的同步,直到所有的生产者线程都完成工作以后,才启动消费者线程。程序如下:

1 #include 
 2 #include 
 3 #include 
 4 #include 
 5 #include 
 6 
 7 #define     MAXNITEMS        1000000
 8 #define     MAXNTHREADS     100
 9 
10 int nitems;
11 
12 struct
13 {
14     pthread_mutex_t     mutex;
15     int                 buff[MAXNITEMS];
16     int                 nput;
17     int                 nval;
18 } shared = {
19     PTHREAD_MUTEX_INITIALIZER
20 };
21 
22 void *produce(void*);
23 void *consume(void*);
24 
25 int main(int argc,char *argv[])
26 {
27     int     i,nthreads,count[MAXNTHREADS];
28     pthread_t tid_produce[MAXNTHREADS],tid_consume;
29     if(argc != 3)
30     {
31         printf("usage: producongs2 <#itmes> <#threads>.\n");
32         exit(0);
33     }
34     nitems = atoi(argv[1]);
35     nthreads = atoi(argv[2]);
36     pthread_setconcurrency(nthreads);  //设置线程并发级别
37     for(i=0;ii)
38     {
39         count[i] = 0;
40         pthread_create(&tid_produce[i],NULL,produce,&count[i]);
41     }
42     for(i=0;i)
43     {
44         pthread_join(tid_produce[i],NULL); //等待线程退出
45         printf("count[%d] = %d\n",i,count[i]);
46     }
47     pthread_create(&tid_consume,NULL,consume,NULL);
48     pthread_join(tid_consume,NULL);  //等待线程退出
49     exit(0);
50 }
51 
52 void *produce(void *arg)
53 {
54     for(; ;)
55     {
56         pthread_mutex_lock(&shared.mutex); //加锁
57         if(shared.nput >= nitems)
58         {
59             pthread_mutex_unlock(&shared.mutex); //释放锁
60             return ;
61         }
62         shared.buff[shared.nput] = shared.nval;
63         shared.nput++;
64         shared.nval++;
65         pthread_mutex_unlock(&shared.mutex); //加锁
66         *((int*) arg) += 1;
67     }
68 }
69 void *consume(void *arg)
70 {
71     int     i;
72     for(i=0;i)
73     {
74         if(shared.buff[i] != i)
75             printf("buff[%d] = %d\n",i,shared.buff[i]);
76     }
77     return;
78 }

程序执行结果如下:

技术分享

例子2:改进例子1,所有生产者线程启动后立即启动消费者线程,这样生产者线程产生数据的同时,消费者线程就能出来它,此时必须同步生产者和消费者,程序如下:

 1 #include 
 2 #include 
 3 #include 
 4 #include 
 5 #include 
 6 
 7 #define     MAXNITEMS        1000000
 8 #define     MAXNTHREADS     100
 9 
10 int nitems;
11 
12 struct
13 {
14     pthread_mutex_t     mutex;
15     int                 buff[MAXNITEMS];
16     int                 nput;
17     int                 nval;
18 } shared = {
19     PTHREAD_MUTEX_INITIALIZER
20 };
21 
22 void *produce(void*);
23 void *consume(void*);
24 void consume_wait(int);
25 int main(int argc,char *argv[])
26 {
27     int     i,nthreads,count[MAXNTHREADS];
28     pthread_t tid_produce[MAXNTHREADS],tid_consume;
29     if(argc != 3)
30     {
31         printf("usage: producongs2 <#itmes> <#threads>.\n");
32         exit(0);
33     }
34     nitems = atoi(argv[1]);
35     nthreads = atoi(argv[2]);
36     pthread_setconcurrency(nthreads+1);
37     //创建生产者线程
38     for(i=0;ii)
39     {
40         count[i] = 0;
41         pthread_create(&tid_produce[i],NULL,produce,&count[i]);
42     }
43     //创建消费者线程
44     pthread_create(&tid_consume,NULL,consume,NULL);
45     for(i=0;i)
46     {
47         pthread_join(tid_produce[i],NULL);
48         printf("count[%d] = %d\n",i,count[i]);
49     }
50     //等待消费者线程退出
51     pthread_join(tid_consume,NULL);
52     exit(0);
53 }
54 
55 void *produce(void *arg)
56 {
57     for(; ;)
58     {
59         pthread_mutex_lock(&shared.mutex);
60         if(shared.nput >= nitems)
61         {
62             pthread_mutex_unlock(&shared.mutex);
63             return ;
64         }
65         shared.buff[shared.nput] = shared.nval;
66         shared.nput++;
67         shared.nval++;
68         pthread_mutex_unlock(&shared.mutex);
69         *((int*) arg) += 1;
70     }
71 }
72 void *consume(void *arg)
73 {
74     int     i;
75     for(i=0;i)
76     {
77         consume_wait(i);
78         if(shared.buff[i] != i)
79             printf("buff[%d] = %d\n",i,shared.buff[i]);
80     }
81     return;
82 }
83 void consume_wait(int i)
84 {
85     for(; ;)  //进行轮询,判断i是否已经由生产者生产
86     {
87         pthread_mutex_lock(&shared.mutex);
88         if(i//i已经生产
89         {
90             pthread_mutex_unlock(&shared.mutex);
91             return; 
92         }
93         pthread_mutex_unlock(&shared.mutex);
94     }
95 }

存在的问题:当消费者获取的条目尚没有准备好时,消费者线程一次次的循环去判断,每次给互斥锁解锁又上锁,这种轮询的办法浪费CPU时间。

2、条件变量

  互斥锁用于上锁,条件变量用于等待,条件变量的使用是与互斥锁共通使用的。

2.1等待与信号发送

  条件变量类型是pthread_cond_t,调用函数如下:

pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *pmutex);

pthread_cond_signal(pthread_cond_t *pcond);

每个条件变量总是有一个互斥锁与之关联。现在采用条件变量实现生产者与消费者问题,程序如下:

 1 #include 
 2 #include 
 3 #include 
 4 #include 
 5 #include 
 6 
 7 #define     MAXNITEMS        1000000
 8 #define     MAXNTHREADS     100
 9 
10 int nitems;
11 
12 struct
13 {
14     pthread_mutex_t     mutex;
15     int                 buff[MAXNITEMS];
16     int                 nput;
17     int                 nval;
18 } shared = {
19     PTHREAD_MUTEX_INITIALIZER
20 };
21 //条件变量
22 struct {
23     pthread_mutex_t mutex;  
24     pthread_cond_t  cond;
25     int nready;
26 }nready = {
27   PTHREAD_MUTEX_INITIALIZER,PTHREAD_COND_INITIALIZER
28 };
29 
30 void *produce(void*);
31 void *consume(void*);
32 
33 int main(int argc,char *argv[])
34 {
35     int     i,nthreads,count[MAXNTHREADS];
36     pthread_t tid_produce[MAXNTHREADS],tid_consume;
37     if(argc != 3)
38     {
39         printf("usage: producongs2 <#itmes> <#threads>.\n");
40         exit(0);
41     }
42     nitems = atoi(argv[1]);
43     nthreads = atoi(argv[2]);
44     pthread_setconcurrency(nthreads+1);
45     for(i=0;ii)
46     {
47         count[i] = 0;
48         pthread_create(&tid_produce[i],NULL,produce,&count[i]);
49     }
50     pthread_create(&tid_consume,NULL,consume,NULL);
51     for(i=0;i)
52     {
53         pthread_join(tid_produce[i],NULL);
54         printf("count[%d] = %d\n",i,count[i]);
55     }
56     pthread_join(tid_consume,NULL);
57     exit(0);
58 }
59 
60 void *produce(void *arg)
61 {
62     printf("producer begins work\n");
63     for(; ;)
64     {
65         pthread_mutex_lock(&shared.mutex);
66         if(shared.nput >= nitems)
67         {
68             pthread_mutex_unlock(&shared.mutex);
69             return ;
70         }
71         shared.buff[shared.nput] = shared.nval;
72         shared.nput++;
73         shared.nval++;
74         pthread_mutex_unlock(&shared.mutex);
75         pthread_mutex_lock(&nready.mutex);
76         if(nready.nready == 0)
77             pthread_cond_signal(&nready.cond); //通知消费者
78         nready.nready++;
79         pthread_mutex_unlock(&nready.mutex);
80         *((int*) arg) += 1;
81     }
82 }
83 void *consume(void *arg)
84 {
85     int     i;
86     printf("consuemer begins work.\n");
87     for(i=0;i)
88     {
89         pthread_mutex_lock(&nready.mutex);
90         while(nready.nready == 0)
91             pthread_cond_wait(&nready.cond,&nready.mutex); //等待生产者
92         nready.nready--;
93         pthread_mutex_unlock(&nready.mutex);
94         if(shared.buff[i] != i)
95             printf("buff[%d] = %d\n",i,shared.buff[i]);
96     }
97     return;
98 }

程序执行结果如下:

技术分享

总的来说,给条件变量发送信号的过程代码如下:

技术分享
struct
{
    pthread_mutex_t    mutex;
    pthread_cond_t       cond;
    //维护本条件的各个变量
}var = {PTHREAD_MUTEX_INITIALIZER,PTHREAD_COND_INITIALIZER,...}

pthread_mutex_lock(&var.mutex);
设置条件为真
pthread_cond_signal(&var.cond);
pthread_mutex_unlock(&var.mutex);
技术分享

测试条件并进入睡眠以等待条件变为真的代码大体如下:

pthread_mutex_lock(&var.mutex);
while(条件为假)
   pthread_cond_wait(&var.cond,&var.mutex);
修改条件
pthread_mutex_unlock(&var.mutex);

 2.2定时等待和广播

  通常pthread_cond_signal只是唤醒等待在相应条件变量上的一个线程,在某些情况下需要唤醒多个线程(例如读写者问题),可以调用pthread_cond_broadcast唤醒阻塞在相应条件变量上的所有线程。pthread_cond_timewait允许线程就阻塞时间设置一个限制值。API如下:

pthread_cond_broadcast(pthread_cond_t *cond);

pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex, const struct timespec *abstime);

linux 线程的同步 二 (互斥锁和条件变量)


推荐阅读
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
  • 本文介绍了指针的概念以及在函数调用时使用指针作为参数的情况。指针存放的是变量的地址,通过指针可以修改指针所指的变量的值。然而,如果想要修改指针的指向,就需要使用指针的引用。文章还通过一个简单的示例代码解释了指针的引用的使用方法,并思考了在修改指针的指向后,取指针的输出结果。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文讲述了作者通过点火测试男友的性格和承受能力,以考验婚姻问题。作者故意不安慰男友并再次点火,观察他的反应。这个行为是善意的玩人,旨在了解男友的性格和避免婚姻问题。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
author-avatar
个信2502894627
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有