使用信号量控制Linux线程同步
线程同步
在现实生活中,有些东西就必须是按顺序执行的,只有我完成了以后,你才能在我的劳动成果上接着干;不能我还没有完成,你就开始干活了。这就是线程同步最直白的解释了。
在进行程序设计时,亦是如此。线程同步,同步的是什么?它同步的是对共享资源(内存区域,公共变量等)或者临界区域的访问。有的时候,这些共享 资源和临界区域,就只能容忍一个线程对它进行操作(读或者写,读操作一般不控制,主要是写操作),这个时候,我们必须要对这些共享资源或者临界区域进行同 步,那么如何对它们进行线程同步呢?
在Linux中主要提供了以下两种方法:
- 用信号量进行同步
- 用互斥量进行同步
不管用什么方法,都是为了线程同步,而这篇文章就对信号量进行线程同步进行详细的总结。
什么是信号量
首先来搞清楚信号量的概念。信号量是一个特殊类型的变量,它可以被增加或减少,但对它的访问都会被保证是原子操作,即使在一个多线程程序中也是 如此。也就是说,如果一个程序中有两个或多个线程试图改变一个信号量的值,系统将保证所有的操作都将依次进行。如果换成普通的变量,来自同一个程序中的不 同线程的冲突操作将会导致不确定的操作。
在工作中,你会遇到两种信号量:二进制信号量和计数信号量。二进制信号量只有0和1两种取值,而计数信号量则有更大的取值范围。如果某个共享资源只能被一个线程访问,那么二进制信号量则是最好的打算;如果有多个线程需要访问共享资源呢,使用计数信号量则是个好的主意。
信号量相关API
在Linux中已经提供了一些相关的API接口来实现信号量,先来和这些接口混一个脸熟,之后再通过一个小例子来熟悉如何使用这些接口。
sem_wait会对信号量的值进行减1操作,但是它会等到信号量的值大于等于1时,才开始进行减1的操作。所以,如果对值为2的信号量调用 sem_wait,线程会继续执行;而如果对为0的信号量进行sem_wait操作,这个函数就会等待,直到有其它线程增加了该信号量的值使其不再为0为 止。
一个小实例
关于信号量操作的API函数都已经介绍完了,现在就通过一个小的程序来用一用这些函数,通过实际的编码,能更好的掌握如何使用这些函数。
接下来代码要完成这样一个功能:
- 主线程从标准输入终端读取输入的内容;
- 子线程1将主线程中输入的内容转换成大写。
来吧,我们就按照上面的功能来编码实现一下。功能简单,我准备这样实现:
- 在主线程初始化信号量为0;
- 当主线程输入完成以后,调用sem_post增加信号量的值;
- 在子线程1中,调用sem_wait,等到信号量的值为1时,就减少信号量的值,然后执行线程中的代码。
好了,编码吧。
好不容易编码完成了,测试一下,效果还是不错的。有小伙伴说了,转小写转大写,直接用 toupper 不就得了么,也是这么个道理。代码就这样了,运行起来,效果还不错,也还能得到之前预期的效果。但是,但是……,多线程程序,永远都是那么多的坑,即使我就写了这么几行代码,我还是嗅到了坏味道。
我先这么分析一下,如果线程1运行到了以下这行代码:
这个时候,我接着在终端中输入内容,这个时候, work_area 保存的内容是否就会变了呢?不妨做个试验,我们把代码修改成这样:
再运行程序,测试一下。你在终端输入快点,然后就发现,输出就变了。不知道我这么说,看官能否明白。简单的说,就是我的线程1还没有将所有字符转换为大写,主线程又开始了输入新的内容,这个时候, work_area 的内容就发生了紊乱,得到的内容并不是我们期望的,同时,信号量的值也会发生错乱。
为什么没有加 sleep(1) 的时候,就没有出现这样的问题呢?我只能说,机器运行的速度快过你认为的速度,加上了 sleep(1) 就相当于认为的给机器降降速。
好了,问题出来了,如何解决呢?
改进版
问题出来了,我们就需要去想办法解决这个问题,由于线程1还没有完成变换,主线程又开始了新的输入。所以,主线程必须要等到线程1完成以后才能 进行新的输入,那么,我们就需要再加入一个信号量,当线程1完成转换时,增加该信号量;主线程完成输入以后,减少该信号量。再来试一试:
这样处理完成以后,所有的输入都会依次进行处理,就不会出现紊乱的问题。所以,对于多线程编程中的这些坑,你是否也踩过呢?希望大家也能和我分享你踩过的坑,共同学习,共同进步。
总结
本来是不想总结的,后来一想,还是挺重要的东西,还是写出来,总结一下吧,再加深一遍记忆。
有人说了,这种Linux线程同步的文章,网上大把,你再写一篇,岂不是多此一举呢?我想说的是,我博客中的文章,每一篇网上都有大把,网上的 是别人总结的,那不是你的;别人总结出来,那是他们经过思考以后才写出来的东西。我博客中的文章是我经过思考写出来的东西,如果你觉的网上有了,就去放弃 自己的总结,那么就相当于放弃了自己的思考。我写文章,一来是自己思考的结晶;二来是为了和大家分享,一起探讨相关知识点。文章,只有自己认真思考过了, 才发现它真的不好写,不要相信你转载以后,那篇文章就是你的了。
2015年,新年新起点。希望大家也能总结出自己的文章。多打开电脑,多打开vim,多敲敲键盘(coding…)。
2015年1月5日 于深圳。