Linux C编程--线程操作2--线程同步详解.pdf

上传人:asd****56 文档编号:70331008 上传时间:2023-01-19 格式:PDF 页数:8 大小:394.04KB
返回 下载 相关 举报
Linux C编程--线程操作2--线程同步详解.pdf_第1页
第1页 / 共8页
Linux C编程--线程操作2--线程同步详解.pdf_第2页
第2页 / 共8页
点击查看更多>>
资源描述

《Linux C编程--线程操作2--线程同步详解.pdf》由会员分享,可在线阅读,更多相关《Linux C编程--线程操作2--线程同步详解.pdf(8页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。

1、D DLUTBruceZhang的专栏LUTBruceZhang的专栏 生活就是需要有创意-例如:Coding&Debug生活就是需要有创意-例如:Coding&Debug 置顶置顶 Linux C编程-线程操作2-线程同步详解 Linux C编程-线程操作2-线程同步详解 分类:Linux C编程 2013-03-06 10:21 465人阅读 评论(0)收藏 举报 互斥锁Linux C编程线程同步条件变量信号量 linux线程同步之互斥 在在windows中,为了让多个线程达到同步的目的,在对于全局变量等大家都要用的资源的使中,为了让多个线程达到同步的目的,在对于全局变量等大家都要用的资源

2、的使用上,通常得保证同时只能由一个线程在用,一个线程没有宣布对它的释放之前,不能够给用上,通常得保证同时只能由一个线程在用,一个线程没有宣布对它的释放之前,不能够给其他线程使用这个变量。在其他线程使用这个变量。在windows里,我们可以用时里,我们可以用时EnterCriticalSection()和和LeaveCriticalSection()函数函数.那么在那么在linux里,有什么类似的机制呢?里,有什么类似的机制呢?这里介绍互斥锁。这里介绍互斥锁。1.申请一个互斥锁申请一个互斥锁 pthread_mutex_t mutex;/申请一个互斥锁申请一个互斥锁 你可以声明多个互斥量。你可以

3、声明多个互斥量。在声明该变量后,你需要调用在声明该变量后,你需要调用pthread_mutex_init()来创建该变量。来创建该变量。pthread_mutex_init的的格式如下:格式如下:int pthread_mutex_init(pthread_mutex_t*mutex,const pthread_mutexattr_t*mutexattr);第一个参数,第一个参数,mutext,也就是你之前声明的那个互斥量,第二个参数为该互斥量的属性。,也就是你之前声明的那个互斥量,第二个参数为该互斥量的属性。属性定义如下:属性定义如下:互斥量分为下面三种:互斥量分为下面三种:l 快速型(快速

4、型(PTHREAD_MUTEX_FAST_NP)。这种类型也是默认的类型。该线程的行为)。这种类型也是默认的类型。该线程的行为正如上面所说的。正如上面所说的。l 递归型(递归型(PTHREAD_MUTEX_RECURSIVE_NP)。如果遇到我们上面所提到的死锁情)。如果遇到我们上面所提到的死锁情况,同一线程循环给互斥量上锁,那么系统将会知道该上锁行为来自同一线程,那么就会同况,同一线程循环给互斥量上锁,那么系统将会知道该上锁行为来自同一线程,那么就会同意线程给该互斥量上锁。意线程给该互斥量上锁。l 错误检测型(错误检测型(PTHREAD_MUTEX_ERRORCHECK_NP)。如果该互斥量

5、已经被上)。如果该互斥量已经被上锁,那么后续的上锁将会失败而不会阻塞,锁,那么后续的上锁将会失败而不会阻塞,pthread_mutex_lock()操作将会返回操作将会返回EDEADLK。可以通过函数可以通过函数 注意以下语句可以做到将一个互斥锁快速初始化为快速型。注意以下语句可以做到将一个互斥锁快速初始化为快速型。pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;2.销毁一个互斥锁销毁一个互斥锁 1pthread_mutex_destroy()用于注销一个互斥锁,用于注销一个互斥锁,API定义如下:定义如下:int pthread_mutex_d

6、estroy(pthread_mutex_t*mutex)销毁一个互斥锁即意味着释放它所占用的资源,且要求锁当前处于开放状态。由于在销毁一个互斥锁即意味着释放它所占用的资源,且要求锁当前处于开放状态。由于在Linux中,互斥锁并不占用任何资源,因此中,互斥锁并不占用任何资源,因此LinuxThreads中的中的pthread_mutex_destroy()除了检查除了检查锁状态以外(锁定状态则返回锁状态以外(锁定状态则返回EBUSY)没有其他动作。)没有其他动作。3.上锁(相当于上锁(相当于windows下的下的EnterCriticalSection)在创建该互斥量之后,你便可以使用它了。要

7、得到互斥量,你需要调用下面的函数:在创建该互斥量之后,你便可以使用它了。要得到互斥量,你需要调用下面的函数:int pthread_mutex_lock(pthread_mutex_t*mutex);该函数用来给互斥量上锁。互斥量一旦被上锁后,其他线程如果想给该互斥量上锁,那么就该函数用来给互斥量上锁。互斥量一旦被上锁后,其他线程如果想给该互斥量上锁,那么就会阻塞在这个操作上。如果在此之前该互斥量已经被其他线程上锁,那么该操作将会一直阻会阻塞在这个操作上。如果在此之前该互斥量已经被其他线程上锁,那么该操作将会一直阻塞在这个地方,直到获得该锁为止。塞在这个地方,直到获得该锁为止。在得到互斥量后,

8、你就可以进入关键代码区了。在得到互斥量后,你就可以进入关键代码区了。4.解锁(相当于解锁(相当于windows下的下的LeaveCriticalSection)在操作完成后,你必须调用下面的函数来给互斥量解锁,也就是前面所说的释放。这样其他在操作完成后,你必须调用下面的函数来给互斥量解锁,也就是前面所说的释放。这样其他等待该锁的线程才有机会获得该锁,否则其他线程将会永远阻塞。等待该锁的线程才有机会获得该锁,否则其他线程将会永远阻塞。int pthread_mutex_unlock(pthread_mutex_t*mutex);5.pthread_mutex_trylock 如果我们不想一直阻塞

9、在这个地方,那么可以调用下面函数:如果我们不想一直阻塞在这个地方,那么可以调用下面函数:int pthread_mutex_trylock(pthread_mutex_t*mutex)如果此时互斥量没有被上锁,那么如果此时互斥量没有被上锁,那么pthread_mutex_trylock()将会返回将会返回0,并会对该互斥量上,并会对该互斥量上锁。如果互斥量已经被上锁,那么会立刻返回锁。如果互斥量已经被上锁,那么会立刻返回EBUSY。注:注:下面介绍一个实例说明上述函数的用法下面介绍一个实例说明上述函数的用法 这是一个简单的读写程序,在这个程序中,一个线程从共享的缓冲区中读数据,另一个线程这是一

10、个简单的读写程序,在这个程序中,一个线程从共享的缓冲区中读数据,另一个线程向共享的缓冲区中写数据。对共享的缓冲区的访问控制是通过使用一个互斥锁来是实现的。向共享的缓冲区中写数据。对共享的缓冲区的访问控制是通过使用一个互斥锁来是实现的。2#include#include#include#include#define FALSE 0#define TRUE 1void readfun();void writefun();char buffer256;int buffer_has_item=0;int retflag=FALSE,i=0;pthread_mutex_t mutex;int main(

11、)void*retval;pthread_t reader;pthread_mutex_init(&mutex,NULL);pthread_create(&reader,NULL,(void*)&readfun,NULL);writefun();pthread_join(reader,&retval);void readfun()while(1)if(retflag)return;pthread_mutex_lock(&mutex);if(buffer_has_item=1)printf(%s,buffer);buffer_has_item=0;pthread_mutex_unlock(&mu

12、tex);void writefun()int i=0;while(1)if(i=10)retflag=TRUE;return;pthread_mutex_lock(&mutex);if(buffer_has_item=0)sprintf(buffer,This is%dn,i+);buffer_has_item=1;pthread_mutex_unlock(&mutex);线程同步之条件变量线程同步之条件变量 与互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一与互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件

13、变量和互斥锁同时使用。个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。3与互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一与互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待量进行同步的一种机制,主要包括两个动作:一个线程等

14、待条件变量的条件成条件变量的条件成立立而挂起;另一个线程使而挂起;另一个线程使条件成立条件成立(给出条件成立信号)。(给出条件成立信号)。条件的检测是在互斥锁的保护下进行的。如果一个条件为假,一个线程自动阻塞,条件的检测是在互斥锁的保护下进行的。如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量

15、可以被用来实现这两进程间的线程同步。进程共享可读写的内存,条件变量可以被用来实现这两进程间的线程同步。使用条件变量之前要先进行初始化。可以在单个语句中生成和初始化一个条件变量使用条件变量之前要先进行初始化。可以在单个语句中生成和初始化一个条件变量如:如:pthread_cond_t my_condition=PTHREAD_COND_INITIALIZER;(用于进程间线程的通信)。可以利用函数(用于进程间线程的通信)。可以利用函数pthread_cond_init动态初始化。动态初始化。条件变量分为两部分条件变量分为两部分:条件和变量条件和变量.条件本身是由互斥量保护的条件本身是由互斥量保护

16、的.线程在改变条件状线程在改变条件状态前先要锁住互斥量态前先要锁住互斥量.它它利用线程间共享的全局变量进行同步的一种机制。利用线程间共享的全局变量进行同步的一种机制。相关的函数如下:相关的函数如下:1 1intintpthread_cond_init(pthread_cond_tpthread_cond_init(pthread_cond_t*cond,pthread_condattr_t*cond,pthread_condattr_t*cond_at*cond_attr);tr);2 2intintpthread_cond_wait(pthread_cond_tpthread_cond_wa

17、it(pthread_cond_t*cond,pthread_mutex_t*cond,pthread_mutex_t*mutex);*mutex);3 3intintpthread_cond_timewait(pthread_cond_tpthread_cond_timewait(pthread_cond_t*cond,pthread_mutex*cond,pthread_mutex*mutex,co*mutex,constnsttimespectimespec*abstime);*abstime);4 4intintpthread_cond_destroy(pthread_cond_tpt

18、hread_cond_destroy(pthread_cond_t*cond);*cond);5 5intintpthread_cond_signal(pthread_cond_tpthread_cond_signal(pthread_cond_t*cond);*cond);6 6intintpthread_cond_broadcast(pthread_cond_tpthread_cond_broadcast(pthread_cond_t*cond);*cond);/觩陪扆朏绅稑皊陁/觩陪扆朏绅稑皊陁塤塤 简要说明:简要说明:(1)初始化.init()或者pthread_cond_t cond

19、=PTHREAD_COND_INITIALIER;属(1)初始化.init()或者pthread_cond_t cond=PTHREAD_COND_INITIALIER;属性置为NULL性置为NULL (2)等待条件成立.pthread_wait,pthread_timewait.wait()释放锁,并阻塞等(2)等待条件成立.pthread_wait,pthread_timewait.wait()释放锁,并阻塞等待条件变量为真待条件变量为真 timewait()设置等待时间,仍未signal,返回ETIMEOUT(加锁保证只有一个线程 timewait()设置等待时间,仍未signal,返回

20、ETIMEOUT(加锁保证只有一个线程wait)wait)(3)激活条件变量:pthread_cond_signal,pthread_cond_broadcast(激活所(3)激活条件变量:pthread_cond_signal,pthread_cond_broadcast(激活所有等待线程)有等待线程)(4)清除条件变量:destroy;无线程等待,否则返回EBUSY(4)清除条件变量:destroy;无线程等待,否则返回EBUSY 详细说明详细说明 1.初始化初始化:条件变量采用的数据类型是条件变量采用的数据类型是pthread_cond_t,在使用之前必须要进行初始化在使用之前必须要进行

21、初始化,这包括两种方式这包括两种方式:静态静态:可以把常量可以把常量PTHREAD_COND_INITIALIZER给静态分配的条件变量给静态分配的条件变量.动态动态:pthread_cond_init函数函数,是释放动态条件变量的内存空间之前是释放动态条件变量的内存空间之前,要用要用pthread_cond_destroy对其进行清理对其进行清理.#include int pthread_cond_init(pthread_cond_t*restrict cond,pthread_condattr_t*restrict attr);int pthread_cond_destroy(pthre

22、ad_cond_t*cond);成功则返回成功则返回0,出错则返回错误编号出错则返回错误编号.当当pthread_cond_init的的attr参数为参数为NULL时时,会创建一个默认属性的条件变会创建一个默认属性的条件变量量;非默认情况以后讨论非默认情况以后讨论.2.等待条件等待条件:42.等待条件等待条件:#include int pthread_cond_wait(pthread_cond_t*restrict cond,pthread_mutex_t*restric mutex);int pthread_cond_timedwait(pthread_cond_t*restrict co

23、nd,pthread_mutex_t*restrict mutex,const struct timespec*restricttimeout);成功则返回成功则返回0,出错则返回错误编号出错则返回错误编号.这两个函数分别是阻塞等待和超时等待这两个函数分别是阻塞等待和超时等待.等待条件函数等待条件变为真等待条件函数等待条件变为真,传递给传递给pthread_cond_wait的互斥量对条件的互斥量对条件进行保护进行保护,调用者把锁住的互斥量传递给函数调用者把锁住的互斥量传递给函数.函数把调用线程放到等待条件的线函数把调用线程放到等待条件的线程列表上程列表上,然后对互斥量解锁然后对互斥量解锁,这

24、两个操作是原子的这两个操作是原子的.这样便关闭了条件检查和线程这样便关闭了条件检查和线程进入休眠状态等待条件改变这两个操作之间的时间通道进入休眠状态等待条件改变这两个操作之间的时间通道,这样线程就不会错过条件这样线程就不会错过条件的任何变化的任何变化.当当pthread_cond_wait返回时返回时,互斥量再次被锁住互斥量再次被锁住.3.通知条件通知条件:#include int pthread_cond_signal(pthread_cond_t*cond);int pthread_cond_broadcast(pthread_cond_t*cond);成功则返回成功则返回0,出错则返回错

25、误编号出错则返回错误编号.这两个函数用于通知线程条件已经满足这两个函数用于通知线程条件已经满足.调用这两个函数调用这两个函数,也称向线程或条件发也称向线程或条件发送信号送信号.必须注意必须注意,一定要在改变条件状态以后再给线程发送信号一定要在改变条件状态以后再给线程发送信号.下面给出一个典例,这个例子是一个典型的生产者/消费者的例子。下面给出一个典例,这个例子是一个典型的生产者/消费者的例子。#include#include#define BUFFER_SIZE 4#define OVER(-1)struct producers /定义生产者条件变量的结构int bufferBUFFER_SI

26、ZE;/定义缓冲区pthread_mutex_tlock;/定义访问缓冲区的互斥锁intreadpos,writepos;/读/写的位置pthread_cond_tnotempty;/缓冲区中有数据时的标记pthread_cond_tnotfull;/缓冲区未满的标记;/初始化缓冲区void init(struct producers*b)pthread_mutex_init(&b-lock,NULL);pthread_cond_init(&b-notempty,NULL);pthread_cond_init(&b-notfull,NULL);b-readpos=0;b-writepos=0;

27、/在缓冲区中存放一个整数void put(struct producers*b,int data)pthread_mutex_lock(&b-lock);5/当缓冲区为满时等待while(b-writepos+1)%BUFFER_SIZE=b-readpos)pthread_cond_wait(&b-notfull,&b-lock);/在返回之前,pthread_cond_wait需要参数b-lock/向缓冲区中写数据,并将写指针向前移动b-bufferb-writepos=data;b-writepos+;if(b-writepos=BUFFER_SIZE)b-writepos=0;/发送当

28、前缓冲区中有数据的信号pthread_cond_signal(&b-notempty);pthread_mutex_unlock(&b-lock);/从缓冲区中读数据并将数据从缓冲区中移走int get(struct producers*b)int data;pthread_mutex_lock(&b-lock);/当缓冲区中无数据时等待while(b-writepos=b-readpos)pthread_cond_wait(&b-notempty,&b-lock);/从缓冲区中读数据,并将指针前移data=b-bufferb-readpos;b-readpos+;if(b-readpos=B

29、UFFER_SIZE)b-readpos=0;/发送当前缓冲区未满的信号pthread_cond_signal(&b-notfull);pthread_mutex_unlock(&b-lock);return data;struct producers buffer;void*producer(void*data)int n;for(n=0;nn,n);put(&buffer,n);put(&buffer,OVER);return NULL;void*consumer(void*data)int d;while(1)d=get(&buffer);if(d=OVER)break;printf(C

30、onsumer:-%dn,d);return NULL;int main()6pthread_t tha,thb;void*retval;init(&buffer);pthread_create(&tha,NULL,producer,0);pthread_create(&thb,NULL,consumer,0);pthread_join(tha,&retval);pthread_join(thb,&retval);return 0;程序说明:程序说明:主进程创建两个线程,一个称为producer,另一个称为consumer。producer向缓冲区中写主进程创建两个线程,一个称为produce

31、r,另一个称为consumer。producer向缓冲区中写整数,当缓冲区中已经写入数据后,就发送缓冲区中有数据的信号。consumer从缓冲区中整数,当缓冲区中已经写入数据后,就发送缓冲区中有数据的信号。consumer从缓冲区中读数据,当consumer从缓冲区中读数据后,就发送当前缓冲区未满的信号。读数据,当consumer从缓冲区中读数据后,就发送当前缓冲区未满的信号。线程同步之信号量线程同步之信号量 sem_wait函数也是一个函数也是一个原子操作原子操作,它的作用是从,它的作用是从信号量信号量的值减去一个的值减去一个“1”,但它永远会先等,但它永远会先等待该信号量为一个非零值才开始

32、做减法。也就是说,如果你对一个值为待该信号量为一个非零值才开始做减法。也就是说,如果你对一个值为2的信号量调用的信号量调用sem_wait(),线程线程将会继续执行,这信号量的值将减到将会继续执行,这信号量的值将减到1。如果对一个值为。如果对一个值为0的信号量调用的信号量调用sem_wait(),这个函数就,这个函数就 会地等待直到有其它线程增加了这个值使它不再是会地等待直到有其它线程增加了这个值使它不再是0为止。如果有为止。如果有两个线程都在两个线程都在sem_wait()中等待同一个信号量变成非零值,那么当它被第三个线程增加中等待同一个信号量变成非零值,那么当它被第三个线程增加 一一个个“

33、1”时,等待线程中只有一个能够对信号量做减法并继续执行,另一个还将处于等待状时,等待线程中只有一个能够对信号量做减法并继续执行,另一个还将处于等待状态。态。sem_post函数的作用是给函数的作用是给信号量信号量的值加上一个的值加上一个“1”,它是一个,它是一个“原子操作原子操作”即同时对同即同时对同一个信号量做加一个信号量做加“1”操作的两个操作的两个线程线程是不会冲突的;而同是不会冲突的;而同 时对同一个文件进行读、加和写操时对同一个文件进行读、加和写操作的两个程序就有可能会引起冲突。信号量的值永远会正确地加一个作的两个程序就有可能会引起冲突。信号量的值永远会正确地加一个“2”因为有两个线

34、因为有两个线程试图改变它。程试图改变它。下面给出实现生产者/消费者的信号量的例子。下面给出实现生产者/消费者的信号量的例子。#include#include#include#define BUFFER_SIZE 4#define OVER(-1)struct producersint bufferBUFFER_SIZE;intreadpos,writepos;sem_tsem_read;sem_tsem_write;void init(struct producers*b)sem_init(&b-sem_write,0,BUFFER_SIZE-1);sem_init(&b-sem_read,0

35、,0);b-readpos=0;b-writepos=0;void put(struct producers*b,int data)sem_wait(&b-sem_write);7b-bufferb-writepos=data;b-writepos+;if(b-writepos=BUFFER_SIZE)b-writepos=0;sem_post(&b-sem_read);int get(struct producers*b)int data;sem_wait(&b-sem_read);data=b-bufferb-readpos;b-readpos+;if(b-readpos=BUFFER_S

36、IZE)b-readpos=0;sem_post(&b-sem_write);return data;struct producers buffer;void*producer(void*data)int n;for(n=0;nn,n);put(&buffer,n);put(&buffer,OVER);return NULL;void*consumer(void*data)int d;while(1)d=get(&buffer);if(d=OVER)break;printf(Consumer:-%dn,d);return NULL;int main()pthread_t tha,thb;void*retval;init(&buffer);pthread_create(&tha,NULL,producer,0);pthread_create(&thb,NULL,consumer,0);pthread_join(tha,&retval);pthread_join(thb,&retval);return 0;8

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 技术资料 > 其他杂项

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知淘文阁网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号© 2020-2023 www.taowenge.com 淘文阁