Linux多线程同步的几种体例
来源:北大青鸟新途麓谷|发布时间:2016-05-21|浏览量:
}编译: g++ -o thread testthread.cpp -lpthread
声明:pthread库不是Linux系统默许的库,毗邻时需求应用静态库libpthread.a,所以在应用pthread_create()树立线程,和挪用pthread_atfork()函数成立fork处置法式时,需求链接该库。在编译中要加 -lpthread参数。
2)前提变量(cond)
支配线程间同享的全局变量中止同步的一种机制。前提变量上的根基支配有:触发前提(当前提变成 true 时);等待前提,挂起线程直到其他线程触发前提。
int pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr);
int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);
int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,const timespec *abstime);
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond); //消弭一切线程的梗阻
(1)初始化.init()或pthread_cond_t cond=PTHREAD_COND_INITIALIER(前者为静态初始化,后者为静态初始化);属性置为NULL
(2)等待前提成立.pthread_wait,pthread_timewait.wait()释放锁,并梗阻等待前提变量为真,timewait()设置等待时分,仍未signal,前往ETIMEOUT(加锁包管只需一个线程wait)
(3)激活前提变量:pthread_cond_signal,pthread_cond_broadcast(激活一切等待线程)
(4)断根前提变量:destroy;无线程等待,不然前往EBUSY
对
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
必然要在mutex的锁定区域内应用。
若是要精确的应用pthread_mutex_lock与pthread_mutex_unlock,请参考
pthread_cleanup_push和pthread_cleanup_pop宏,它能够或许在线程被cancel的时辰精确的释放mutex!
别的,posix1尺度说,pthread_cond_signal与pthread_cond_broadcast无需推敲挪用线程是不是是mutex的具有者,也就是说,能够在lock与unlock之外的区域挪用。若是我们对换用行动不关心,那末请在lock区域以外挪用吧。
声明:
(1)pthread_cond_wait 主动解锁互斥量(犹如实行了pthread_unlock_mutex),并等待前提变量触发。这时分线程挂起,不占用CPU时分,直到前提变量被触发(变量为ture)。在挪用 pthread_cond_wait之前,应用法式必需加锁互斥量。pthread_cond_wait函数前往前,主动从头对互斥量加锁(犹如实行了pthread_lock_mutex)。
(2)互斥量的解锁和在前提变量上挂起都是主动中止的。是以,在前提变量被触发前,若是一切的线程都要对互斥量加锁,这类机制可包管在线程加锁互斥量和进进等待前提变量时期,前提变量不被触发。前提变量要和互斥量相联络,以免呈现前提竞争——个线程豫备等待一个前提变量,当它在真正进进等待之前,另外一个线程刚好触发了该前提(前提知足旌旗灯号有可以在测试前提和挪用pthread_cond_wait函数(block)之间被收回,从而构成无量制的等待)。
(3)pthread_cond_timedwait 和 pthread_cond_wait 一样,主动解锁互斥量及等待前提变量,但它还限制了等待时分。若是在abstime指定的时分内cond未触发,互斥量mutex被从头加锁,且pthread_cond_timedwait前往缺点 ETIMEDOUT。abstime 参数指定一个尽对时分,时分原点与 time 和gettimeofday 不异:abstime = 0 暗示 1970年1月1日00:00:00 GMT。
(4)pthread_cond_destroy 烧毁一个前提变量,释放它具有的资本。进进 pthread_cond_destroy 之前,必需没有在该前提变量上等待的线程。
(5)前提变量函数不是异步旌旗灯号安全的,不应当在旌旗灯号处置法式中中止挪用。出格要注重,若是在旌旗灯号处置法式中挪用 pthread_cond_signal 或pthread_cond_boardcast 函数,可以致使挪用线程死锁。
示例法式1
#include stdio.h #include pthread.h #include "stdlib.h"#include "unistd.h"pthread_mutex_t mutex;pthread_cond_t cond;void hander(void *arg) free(arg); (void)pthread_mutex_unlock( mutex);void *thread1(void *arg) pthread_cleanup_push(hander, mutex); while(1) printf("thread1 is runningn"); pthread_mutex_lock( mutex); pthread_cond_wait( cond, mutex); printf("thread1 applied the conditionn"); pthread_mutex_unlock( mutex); sleep(4); pthread_cleanup_pop(0); void *thread2(void *arg) while(1) printf("thread2 is runningn"); pthread_mutex_lock( mutex); pthread_cond_wait( cond, mutex); printf("thread2 applied the conditionn"); pthread_mutex_unlock( mutex); sleep(1); int main() pthread_t thid1,thid2; printf("condition variable study!n"); pthread_mutex_init( mutex,NULL); pthread_cond_init( cond,NULL); pthread_create( thid1,NULL,thread1,NULL); pthread_create( thid2,NULL,thread2,NULL); sleep(1); pthread_cond_signal( cond); }while(1); sleep(20); pthread_exit(0); return 0;}
示例法式2:
#include pthread.h #include unistd.h #include "stdio.h"#include "stdlib.h"static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; struct node int n_number; struct node *n_next; } *head = NULL; /*[thread_func]*/ static void cleanup_handler(void *arg) printf("Cleanup handler of second thread./n"); free(arg); (void)pthread_mutex_unlock( mtx); static void *thread_func(void *arg) struct node *p = NULL; pthread_cleanup_push(cleanup_handler, p); while (1) //这个mutex首要是用来包管pthread_cond_wait的并发性 pthread_mutex_lock( mtx); while (head == NULL) //这个while要出格声明一下,单个pthread_cond_wait功用很圆满,为什么 //这里要有一个while (head == NULL)呢?由于pthread_cond_wait里的线 //程可以会被不测叫醒,若是这个时辰head != NULL,则不是我们想要的环境。 //这个时辰,应当让线程连续进进pthread_cond_wait // pthread_cond_wait会先消弭之前的pthread_mutex_lock锁定的mtx, //然后梗阻在等待对列里休眠,直到再次被叫醒(年夜大都环境下是等待的前提成立 //而被叫醒,叫醒后,该进程会先锁定先pthread_mutex_lock( mtx);,再读取资本 //用这个流程是比力明晰的/*block-- unlock-- wait() return-- lock*/ pthread_cond_wait( cond, mtx); p = head; head = head- n_next; printf("Got %d from front of queue/n", p- n_number); free(p); pthread_mutex_unlock( mtx); //临界区数据支配终了,释放互斥锁 pthread_cleanup_pop(0); return 0; int main(void) pthread_t tid; int i; struct node *p; //子线程会一向等待资本,近似出产者和消费者,可是这里的消费者可所以多个消费者,而 //不但仅支撑浅显的单个消费者,这个模子固然俭朴,可是很壮大 pthread_create( tid, NULL, thread_func, NULL); sleep(1); for (i = 0; i i++) p = (struct node*)malloc(sizeof(struct node)); p- n_number = i; pthread_mutex_lock( mtx); //需求支配head这个临界资本,先加锁, p- n_next = head; head = p; pthread_cond_signal( cond); pthread_mutex_unlock( mtx); //解锁 sleep(1); printf("thread 1 wanna end the line.So cancel thread 2./n"); //关于pthread_cancel,有一点额定的声明,它是从内部终止子线程,子线程会在比来的消除点,参加 //线程,而在我们的代码里,比来的消除点必定就是pthread_cond_wait()了。 pthread_cancel(tid); pthread_join(tid, NULL); printf("All done -- exiting/n"); return 0; }
3)旌旗灯号量
犹如进程一样,线程也可以经由过程旌旗灯号量来完成通讯,固然是轻量级的。
旌旗灯号量函数的名字都以 sem_ 打头。线程应用的根基旌旗灯号量函数有四个。
#include semaphore.h
int sem_init (sem_t *sem , int pshared, unsigned int value);
这是对由sem指定的旌旗灯号量中止初始化,设置好它的同享选项(linux 只支撑为0,即暗示它是以后进程的局部旌旗灯号量),然后给它一个初始值VALUE。
两个原子支配函数:
int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);
这两个函数都要用一个由sem_init挪用初始化的旌旗灯号量对象的指针做参数。
sem_post:给旌旗灯号量的值加1;
sem_wait:给旌旗灯号量减1;对一个值为0的旌旗灯号量挪用sem_wait,这个函数将会等待直到有其它线程使它不再是0为止。
int sem_destroy(sem_t *sem);
这个函数的感化是再我们用完旌旗灯号量后都它中止清算。归还自身据有的一切资本。
示例代码:
#include stdlib.h #include stdio.h #include unistd.h #include pthread.h #include semaphore.h #include errno.h #define return_if_fail(p) if((p) == 0){printf ("[%s]:func error!/n", __func__);return;} typedef struct _PrivInfo sem_t s1; sem_t s2; time_t end_time; }PrivInfo; static void info_init (PrivInfo* thiz); static void info_destroy (PrivInfo* thiz); static void* pthread_func_1 (PrivInfo* thiz); static void* pthread_func_2 (PrivInfo* thiz); int main (int argc, char** argv) pthread_t pt_1 = 0; pthread_t pt_2 = 0; int ret = 0; PrivInfo* thiz = NULL; thiz = (PrivInfo* )malloc (sizeof (PrivInfo)); if (thiz == NULL) printf ("[%s]: Failed to malloc priv./n"); return -1; info_init (thiz); ret = pthread_create ( pt_1, NULL, (void*)pthread_func_1, thiz); if (ret != 0) perror ("pthread_1_create:"); ret = pthread_create ( pt_2, NULL, (void*)pthread_func_2, thiz); if (ret != 0) perror ("pthread_2_create:"); pthread_join (pt_1, NULL); pthread_join (pt_2, NULL); info_destroy (thiz); return 0; static void info_init (PrivInfo* thiz) return_if_fail (thiz != NULL); thiz- end_time = time(NULL) + 10; sem_init ( thiz- s1, 0, 1); sem_init ( thiz- s2, 0, 0); return; static void info_destroy (PrivInfo* thiz) return_if_fail (thiz != NULL); sem_destroy ( thiz- sem_destroy ( thiz- free (thiz); thiz = NULL; return; static void* pthread_func_1 (PrivInfo* thiz) return_if_fail (thiz != NULL); while (time(NULL) thiz- end_time) sem_wait ( thiz- printf ("pthread1: pthread1 get the lock./n"); sem_post ( thiz- printf ("pthread1: pthread1 unlock/n"); sleep (1); return; static void* pthread_func_2 (PrivInfo* thiz) return_if_fail (thiz != NULL); while (time (NULL) thiz- end_time) sem_wait ( thiz- printf ("pthread2: pthread2 get the unlock./n"); sem_post ( thiz- printf ("pthread2: pthread2 unlock./n"); sleep (1); return; }
通 过实行成果后,能够看出,会先实行线程二的函数,然后再实行线程一的函数。它们两就完成了同步。在上年夜学的时辰,固然对这些概念明白,可都没有理论过,所 以有时辰时分一久就会恍惚乃至健忘,到了任务若是还连结这么一种情况,那就太恐惧了。固然此刻里面的手艺在不竭的转变卦新,可是不管怎样变,其焦点手艺还 是依旧的,所以我们必需求打好自身的基本,再进修其他新的常识,那时辰再学新的常识也会觉得比力俭朴的。
上一篇:返回列表
下一篇:有关Linux日记阐发的具体先容
扫码关注微信公众号了解更多详情
跟技术大咖,专业导师一起交流学习