在同时执行的程序中,我们看到两个线程同时执行。线程之间的切换的方法是非常笨拙。
好在专门有一组设计好的函数为我们提供了更好的控制线程执行的访问代码临界区域的方。
两种基本方法:;
(1)信号量:它的代码如同看守一段代码的看门人一样;
(2)互斥量:如同保护代码段的一个互斥设备。
这两种方法是相似的,事实上,我们是可以互相通过对方来实现的。
但是不同的场景使用不同的方式会语义会不一样,所以,需要选择;
例如:如果想控制任一时刻只能有一个线程可以访问一些共享内存,使用互斥量就要自然很多。如果控制一对相同对象的访问时——比如从5条用的电话线中分配1条给某个线程的情况,就更适合使用计数信号量。
用信号量进行同步##
有两组接口函数用于信号量。
一组取自于POSIX 的实时扩展,用于##线程同步。
一组被称为系统V信号量,常常用于##进程的同步##(14章介绍)
信号量的提出者 —— 荷兰计算机科学家Dijkstra .
信号量是一个特殊类型的变量,它可以被增加或减少,但对其的关键访问被保证是原子操作,及时在一个多线程程序中也是如此。这意味着如果一个程序中有两个(或更多)的线程试图改变一个信号量的值,系统将保证所有的操作都将依次进行。但如果是普通变量,来自同一程序中的不同的线程的冲突操作所导致的结果将是不确定的。
这里讲的是简单的信号量 —— 二进制信号量,它只有0和1两种取值。还有一种通用的信号量—— 计数信号量(可以取更大的取值范围)。
信号量一般常用来保护一段代码,使其每次只能够被一个执行线程运行,
要完成这个工作,就要使用二进制信号量;有时允许有限数目的线程执行一段指定的代码,就需要用到技术信号量。
*** 这里注意 “信号” 和“信号量” 是两个不同的概念。
信号量函数一般是以sem_开头,而不像大多数线程函数那样以pthread_开头。线程中使用的基本信号量函数有4个。信号量通常使用sem_init函数创建。 头文件是: semaphore.h
int sem_init(sem_t *sem, int pshared, unsigned int value);
这个函数初始化由sem指向的信号量对象,设置它的共享选项,并给它一个初始的整数值。
pshared 参数控制信号量的类型,如果其值是0,就表示这个信号量是当前进程的局部信号量,否则,这个信号量就可以在多个进程之间共享。(我们这里只对不能在进程间共享的信号量感兴趣)
(linux你使用的版本可能不支持这种共享,给pshared 参数传递一个非零的值将导致调用失败)
下面的两个函数控制信号量的值:
int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);
int sem_destroy(sem_t *sem);
参数都是指针作为参数,该指针指向的对象是由sem_init 调用初始化的信号量。
sem_post 以原子操作的 方式给信号量的值增加1。
原子操作:如果两个线程企图同时给一个信号量加1,它们之间不会互相干扰。而两个程序同时对同一个文件进行读取、增加、写入操作时可能会引起冲突。信号量的值总是会被正确的加2,因为有两个线程试图改变它。
sem_wait 函数以原子操作的方式将信号量减1 ,但它会等待直到信号量有个非零值才会开始减法操作。对值为0信号量调用sem_wait ,这个线程会等待,直到其他线程增加了信号量的值使其不再为0为止。还有另外一个信号量函数sem_trywait ,它是sem_wait 的非阻塞版本。
sem_destroy : 用完信号量后对它进行清理。清理该信号量拥有的所有资源。若是企图清理的信号量正被一些线程等待,就会收到一个错误。成功就会返回0。