您的位置:首页 > 运维架构 > Linux

linux下线程的同步和互斥

2015-10-22 20:17 405 查看

线程的同步和互斥

在操作系统中引入线程以及线程并发性的概念后,增加了系统的效率,同时,由于资源有限导致线程之间的资源竞争和共享,因此产生一个问题,例如,当多个线程同时申请一台打印机的时候,如果不加限制,很可能使多个线程的输出结果交织在一起,产生错误的打印。下面我来为大家主要介绍并发执行的线程使用临界资源时,同步与互斥的以及控制方法。

1.临界资源的概念:多个线程不能同时使用的资源称为临界资源CR(Critical Resource)。临界资源可以是一些独占设备,比如打印机等,也可以是一些共享变量,表格,链表等。

2.临界区:不论硬件临界资源还是软件临界资源,多个线程必须互斥的对其进行访问。每个线程中访问临界资源的那段代码称为临界区CS(Critical Section)。每个线程在进入临界区以前,应该对欲访问的临界资源进行检查,看他是否正在被访问。若是,则该线程不能进入临界区,若否,则该线程可以进入临界区对该资源进行访问,并设置只在被访问的标志。

3.线程互斥的概念:线程互斥是指多个线程不能同时使用同一个临界资源,即两个或两个以上线程必须互斥的使用临界资源,当然不能同时进入临界区。如:现在系统中有一台打印机,两个线程都要使用,为了保证打印结果正确,只能一个线程用完后另一个再用。在一个线程使用时必须做上占用标记,用完以后必须清楚标记。这样,另一个线程才会知道打印机空闲,然后开始使用。这两个逻辑上完全独立,毫无关系的线程,由于竞争同一个资源而相互制约,这就称为线程的互斥。

4.线程同步的概念:线程同步是指有协作关系的线程之间不断地调整他们之间的相对速度或者执行过程,以保证临界资源的合理利用和线程的顺利执行。实现线程同步的机制称为线程同步机制。不同的同步机制实现同步的方法可能不同,但一般都借助一个中间媒介来实现,如信号量,锁等。在现实中存在线程同步关系的线程很多,如:两个线程合作使用同一缓冲区,设线程A负责向缓冲区中输入数据,线程B负责缓冲区中输出数据。当线程A将数据缓冲区输满时,则只有当线程B将该数据输出后,线程
A才可以继续使用该缓冲区,否则将造成数据丢失。此时,线程A,B之间就形成了同步关系。

同步机制遵循的规则:

(1)空闲让进:并发线程中某个线程不再临界区时,不阻止其它线程进入临界区。

(2)忙则等待:只允许同一时刻一个线程进入临界区,其它欲进入临界区的线程等待。

(3)有限等待:欲访问临界区的线程要在有限的时间内访问到临界区,避免陷入“死等”状态。

(4)让权等待:当线程不能进入自己的临界区的时候,应立即释放CPU,以避免线程陷入“忙等”状态。

锁机制:实现互斥的一种方式是采用锁机制,即提供上锁(Lock)和开锁(Unlock)原语,以及一个锁变量w或者锁位(1bit)。当线程进入临界区之前,首先测试w的状态,w=1,表示上锁,意味着该资源以及占用;w=0表示开锁,意味着该资源以及空闲,可以进入临界区使用临界资源。

这种利用锁机制解决互斥访问的方法安全可靠,但是效率不高。因此出现了信号量。

信号量:(Semaphore)也叫信号灯,是在信号同步机制中用于实现线程的同步和互斥有效的数据结构。信号量有很多种类型的数据结构。如:整型信号量、记录型信号量、AND型信号量、及信号量集等。在这里我们主要介绍整型信号量,其它类型信号量读者可以自己查阅相关资料。整型信号量是信号量最简单的类型,也是各种信号量类型中必须包含的类型,整型信号量的数值表示当前系统中可用的该类型临界资源的数量。如整型信号量s,则s值得意义为:

s>0:表示系统中空闲的该类临界资源的个数;

s=0:表示系统中该类临界资源正好全部被占用,而且没有线程在等待该资源。

s<0:s的绝对值表示系统中等待该类临界资源的线程的个数。

wait(s)和signal(s)的原语操作可以描述为:

wait(s): while s<=0 该线程等待

s:s=s-1;

Signal(s): s: s=s+1;

信号量机制:信号量机制中,申请和释放临界资源的两个原语操作为wait操作和signal操作,有时也称为P操作和V操作。

线程同步问题的一个典型案例:生产者—消费者问题,这是一个著名的线程同步问题。描述为:有一群生产者线程在生产产品,并将这些产品提供给消费者线程去消费,为方便生产者线程与消费者线程能并发执行,在两者之间设置一个具有n个缓冲区的缓冲池,生产者线程将生产一个商品并放入到一个缓冲区中,消费者线程可以从一个缓冲区中取走一个商品消费。

问题分析:显然消费者线程不能到一个空的缓冲区中去取商品,生产者线程也不能将产品放入一个已经装满产品且尚未取走的缓冲区中。

一个数组表示具有n和缓冲区的缓冲池,设输入输出指针分别为in和out,in表示下一个可存放产品的缓冲区,out表示下一个可取得产品的缓冲区。考虑是循环缓冲池,每当生产者线程向缓冲池中生产一个产品时,in=(in+1)mod n;每当消费者线程从缓冲池中取走一个产品后,out=(out+1)mod n。故当out=in时,缓冲池空,当out=(in+1)mod n时,表示缓冲池满。

0

1

2





i





n-2

n-1

生产者消费者问题缓冲池

为解决生产者—消费者问题各线程的同步问题,设以下信号量:

sem_mutex:互斥使用缓冲池信号量,初始情况下无线程进入缓冲池,因此初值设为1;

empty_sem_mutex:使用缓冲池中空缓冲区的信号量,由于初始情况下所有缓冲区为空,因此设置为n;

full_sem_mutex:使用缓冲池中满缓冲区的信号量,由于初始情况下所有缓冲区为空,因此设置为0;

程序:#include<pthread.h>

#include<stdio.h>

#include<stdlib.h>

#include<semaphore.h>

#define BUFF_SIZE10

intbuffer[BUFF_SIZE];

int in=0;

int out=0;

int p=1;

sem_t sem_mutex;

sem_tempty_sem_mutex;

sem_tfull_sem_mutex;

void *Producer()

{

while(1)

{

sem_wait(&empty_sem_mutex);

sem_wait(&sem_mutex);

if(p>10)

p=1;

buffer[in]=p++;

printf("\nproduce%d\n",buffer[in]);

if(out==(in=(in+1)%BUFF_SIZE))

{

printf("buffer isfull\n");

}

sem_post(&sem_mutex);

sem_post(&full_sem_mutex);

}

}

void *Consumer()

{

while(1)

{

sem_wait(&full_sem_mutex);

sem_wait(&sem_mutex);

printf("\nconsume%d\n",buffer[out]);

if((out=(out+1)%BUFF_SIZE)==in)

{

printf("buffer isempty\n");

}

sem_post(&sem_mutex);

sem_post(&empty_sem_mutex);

}

}

int main()

{

pthread_t ptid,ctid;

sem_init(&sem_mutex,0,1);//unlocksem_mutex=1;

sem_init(&empty_sem_mutex,0,10);//unlockempty_sem_mutex=10;

sem_init(&full_sem_mutex,0,0);//lock

printf("\nmutex initedsuccess!");

if(pthread_create(&ptid,NULL,Producer,NULL))

{

printf("\n Error creatingthread1");

exit(1);

}

else

{

printf("\ncreate thread1success!");

}

if(pthread_create(&ctid,NULL,Consumer,NULL))

{

printf("\n Error creatingthread2");

exit(1);

}

else

{

printf("\ncreate thread2success!");

}

if(pthread_join(ptid,NULL))

{

printf("\n Error joining threadproducer");

exit(1);

}

if(pthread_join(ctid,NULL))

{

printf("\n Error joining threadconsumer");

exit(1);

}

sem_destroy(&empty_sem_mutex);

sem_destroy(&full_sem_mutex);

sem_destroy(&sem_mutex);

//exit the main thread

pthread_exit(NULL);

return 1;

}

程序运行部分结果(图一和二衔接着):




总结:

(1)在每个程序中用于互斥的wait(mutex)和signal(mutex)必须成对出现,即对临界资源的使用前必须申请,使用后必须释放。

(2)对资源信号量empty_sem_mutex和full_sem_mutex的wait和signal操作也需要成对出现,但它们分别处于不同的进程中,以保证生产者线程和消费者线程的同步(若n=1,则生产者和消费者线程只能严格的交替执行,若n>1,则在消费者线程不执行的前提下,生产者线程最多可重复执行n次;反之消费者也同样)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: