Linux 线程同步的三种方法

http://blog.csdn.net/zsf8701/article/details/7844316

线程的最大特点是资源的共享性,但资源共享中的同步问题是多线程编程的难点。linux下提供了多种方式来处理线程同步,最常用的是互斥锁、条件变量和信号量。

一、互斥锁(mutex)

通过锁机制实现线程间的同步。

  1. 初始化锁。在Linux下,线程的互斥量数据类型是pthread_mutex_t。在使用前,要对它进行初始化。
    静态分配:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    动态分配:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_attr_t *mutexattr);
  2. 加锁。对共享资源的访问,要对互斥量进行加锁,如果互斥量已经上了锁,调用线程会阻塞,直到互斥量被解锁。
    int pthread_mutex_lock(pthread_mutex *mutex);
    int pthread_mutex_trylock(pthread_mutex_t *mutex);
  3. 解锁。在完成了对共享资源的访问后,要对互斥量进行解锁。
    int pthread_mutex_unlock(pthread_mutex_t *mutex);
  4. 销毁锁。锁在是使用完成后,需要进行销毁以释放资源。
    int pthread_mutex_destroy(pthread_mutex *mutex);
[csharp] view plaincopy
  1. #include <cstdio>  
  2. #include <cstdlib>  
  3. #include <unistd.h>  
  4. #include <pthread.h>  
  5. #include "iostream"  
  6. using namespace std;  
  7. pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;  
  8. int tmp;  
  9. void* thread(void *arg)  
  10. {  
  11.     cout << "thread id is " << pthread_self() << endl;  
  12.     pthread_mutex_lock(&mutex);  
  13.     tmp = 12;  
  14.     cout << "Now a is " << tmp << endl;  
  15.     pthread_mutex_unlock(&mutex);  
  16.     return NULL;  
  17. }  
  18. int main()  
  19. {  
  20.     pthread_t id;  
  21.     cout << "main thread id is " << pthread_self() << endl;  
  22.     tmp = 3;  
  23.     cout << "In main func tmp = " << tmp << endl;  
  24.     if (!pthread_create(&id, NULL, thread, NULL))  
  25.     {  
  26.         cout << "Create thread success!" << endl;  
  27.     }  
  28.     else  
  29.     {  
  30.         cout << "Create thread failed!" << endl;  
  31.     }  
  32.     pthread_join(id, NULL);  
  33.     pthread_mutex_destroy(&mutex);  
  34.     return 0;  
  35. }  
  36. //编译:g++ -o thread testthread.cpp -lpthread  

二、条件变量(cond)

互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。条件变量分为两部分: 条件和变量。条件本身是由互斥量保护的。线程在改变条件状态前先要锁住互斥量。条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。条件的检测是在互斥锁的保护下进行的。如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量可以被用来实现这两进程间的线程同步。

  1. 初始化条件变量。
    静态态初始化,pthread_cond_t cond = PTHREAD_COND_INITIALIER;
    动态初始化,int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
  2. 等待条件成立。释放锁,同时阻塞等待条件变量为真才行。timewait()设置等待时间,仍未signal,返回ETIMEOUT(加锁保证只有一个线程wait)
    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);
  3. 激活条件变量。pthread_cond_signal,pthread_cond_broadcast(激活所有等待线程)
    int pthread_cond_signal(pthread_cond_t *cond);
    int pthread_cond_broadcast(pthread_cond_t *cond); //解除所有线程的阻塞
  4. 清除条件变量。无线程等待,否则返回EBUSY
    int pthread_cond_destroy(pthread_cond_t *cond);
[cpp] view plaincopy
  1. #include <stdio.h>  
  2. #include <pthread.h>  
  3. #include "stdlib.h"  
  4. #include "unistd.h"  
  5. pthread_mutex_t mutex;  
  6. pthread_cond_t cond;  
  7. void hander(void *arg)  
  8. {  
  9.     free(arg);  
  10.     (void)pthread_mutex_unlock(&mutex);  
  11. }  
  12. void *thread1(void *arg)  
  13. {  
  14.     pthread_cleanup_push(hander, &mutex);  
  15.     while(1)  
  16.     {  
  17.         printf("thread1 is running\n");  
  18.         pthread_mutex_lock(&mutex);  
  19.         pthread_cond_wait(&cond, &mutex);  
  20.         printf("thread1 applied the condition\n");  
  21.         pthread_mutex_unlock(&mutex);  
  22.         sleep(4);  
  23.     }  
  24.     pthread_cleanup_pop(0);  
  25. }  
  26. void *thread2(void *arg)  
  27. {  
  28.     while(1)  
  29.     {  
  30.         printf("thread2 is running\n");  
  31.         pthread_mutex_lock(&mutex);  
  32.         pthread_cond_wait(&cond, &mutex);  
  33.         printf("thread2 applied the condition\n");  
  34.         pthread_mutex_unlock(&mutex);  
  35.         sleep(1);  
  36.     }  
  37. }  
  38. int main()  
  39. {  
  40.     pthread_t thid1,thid2;  
  41.     printf("condition variable study!\n");  
  42.     pthread_mutex_init(&mutex, NULL);  
  43.     pthread_cond_init(&cond, NULL);  
  44.     pthread_create(&thid1, NULL, thread1, NULL);  
  45.     pthread_create(&thid2, NULL, thread2, NULL);  
  46.     sleep(1);  
  47.     do  
  48.     {  
  49.         pthread_cond_signal(&cond);  
  50.     }while(1);  
  51.     sleep(20);  
  52.     pthread_exit(0);  
  53.     return 0;  
  54. }  
[cpp] view plaincopy
  1. #include <pthread.h>  
  2. #include <unistd.h>  
  3. #include "stdio.h"  
  4. #include "stdlib.h"  
  5. static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;  
  6. static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;  
  7. struct node  
  8. {  
  9.     int n_number;  
  10.     struct node *n_next;  
  11. }*head = NULL;  
  12.   
  13. static void cleanup_handler(void *arg)  
  14. {  
  15.     printf("Cleanup handler of second thread./n");  
  16.     free(arg);  
  17.     (void)pthread_mutex_unlock(&mtx);  
  18. }  
  19. static void *thread_func(void *arg)  
  20. {  
  21.     struct node *p = NULL;  
  22.     pthread_cleanup_push(cleanup_handler, p);  
  23.     while (1)  
  24.     {  
  25.         //这个mutex主要是用来保证pthread_cond_wait的并发性  
  26.         pthread_mutex_lock(&mtx);  
  27.         while (head == NULL)  
  28.         {  
  29.             //这个while要特别说明一下,单个pthread_cond_wait功能很完善,为何  
  30.             //这里要有一个while (head == NULL)呢?因为pthread_cond_wait里的线  
  31.             //程可能会被意外唤醒,如果这个时候head != NULL,则不是我们想要的情况。  
  32.             //这个时候,应该让线程继续进入pthread_cond_wait  
  33.             // pthread_cond_wait会先解除之前的pthread_mutex_lock锁定的mtx,  
  34.             //然后阻塞在等待对列里休眠,直到再次被唤醒(大多数情况下是等待的条件成立  
  35.             //而被唤醒,唤醒后,该进程会先锁定先pthread_mutex_lock(&mtx);,再读取资源  
  36.             //用这个流程是比较清楚的  
  37.             pthread_cond_wait(&cond, &mtx);  
  38.             p = head;  
  39.             head = head->n_next;  
  40.             printf("Got %d from front of queue/n", p->n_number);  
  41.             free(p);  
  42.         }  
  43.         pthread_mutex_unlock(&mtx); //临界区数据操作完毕,释放互斥锁  
  44.     }  
  45.     pthread_cleanup_pop(0);  
  46.     return 0;  
  47. }  
  48. int main(void)  
  49. {  
  50.     pthread_t tid;  
  51.     int i;  
  52.     struct node *p;  
  53.     //子线程会一直等待资源,类似生产者和消费者,但是这里的消费者可以是多个消费者,而  
  54.     //不仅仅支持普通的单个消费者,这个模型虽然简单,但是很强大  
  55.     pthread_create(&tid, NULL, thread_func, NULL);  
  56.     sleep(1);  
  57.     for (i = 0; i < 10; i++)  
  58.     {  
  59.         p = (struct node*)malloc(sizeof(struct node));  
  60.         p->n_number = i;  
  61.         pthread_mutex_lock(&mtx); //需要操作head这个临界资源,先加锁,  
  62.         p->n_next = head;  
  63.         head = p;  
  64.         pthread_cond_signal(&cond);  
  65.         pthread_mutex_unlock(&mtx); //解锁  
  66.         sleep(1);  
  67.     }  
  68.     printf("thread 1 wanna end the line.So cancel thread 2./n");  
  69.     //关于pthread_cancel,有一点额外的说明,它是从外部终止子线程,子线程会在最近的取消点,退出  
  70.     //线程,而在我们的代码里,最近的取消点肯定就是pthread_cond_wait()了。  
  71.     pthread_cancel(tid);  
  72.     pthread_join(tid, NULL);  
  73.     printf("All done -- exiting/n");  
  74.     return 0;  
  75. }  

三、信号量(sem)

如同进程一样,线程也可以通过信号量来实现通信,虽然是轻量级的。信号量函数的名字都以"sem_"打头。线程使用的基本信号量函数有四个。

  1. 信号量初始化。
    int sem_init (sem_t *sem , int pshared, unsigned int value);
    这是对由sem指定的信号量进行初始化,设置好它的共享选项(linux 只支持为0,即表示它是当前进程的局部信号量),然后给它一个初始值VALUE。
  2. 等待信号量。给信号量减1,然后等待直到信号量的值大于0。
    int sem_wait(sem_t *sem);
  3. 释放信号量。信号量值加1。并通知其他等待线程。
    int sem_post(sem_t *sem);
  4. 销毁信号量。我们用完信号量后都它进行清理。归还占有的一切资源。
    int sem_destroy(sem_t *sem);
[cpp] view plaincopy
  1. #include <stdlib.h>  
  2. #include <stdio.h>  
  3. #include <unistd.h>  
  4. #include <pthread.h>  
  5. #include <semaphore.h>  
  6. #include <errno.h>  
  7. #define return_if_fail(p) if((p) == 0){printf ("[%s]:func error!/n", __func__);return;}  
  8. typedef struct _PrivInfo  
  9. {  
  10.     sem_t s1;  
  11.     sem_t s2;  
  12.     time_t end_time;  
  13. }PrivInfo;  
  14.   
  15. static void info_init (PrivInfo* thiz);  
  16. static void info_destroy (PrivInfo* thiz);  
  17. static void* pthread_func_1 (PrivInfo* thiz);  
  18. static void* pthread_func_2 (PrivInfo* thiz);  
  19.   
  20. int main (int argc, char** argv)  
  21. {  
  22.     pthread_t pt_1 = 0;  
  23.     pthread_t pt_2 = 0;  
  24.     int ret = 0;  
  25.     PrivInfo* thiz = NULL;  
  26.     thiz = (PrivInfo* )malloc (sizeof (PrivInfo));  
  27.     if (thiz == NULL)  
  28.     {  
  29.         printf ("[%s]: Failed to malloc priv./n");  
  30.         return -1;  
  31.     }  
  32.     info_init (thiz);  
  33.     ret = pthread_create (&pt_1, NULL, (void*)pthread_func_1, thiz);  
  34.     if (ret != 0)  
  35.     {  
  36.         perror ("pthread_1_create:");  
  37.     }  
  38.     ret = pthread_create (&pt_2, NULL, (void*)pthread_func_2, thiz);  
  39.     if (ret != 0)  
  40.     {  
  41.         perror ("pthread_2_create:");  
  42.     }  
  43.     pthread_join (pt_1, NULL);  
  44.     pthread_join (pt_2, NULL);  
  45.     info_destroy (thiz);  
  46.     return 0;  
  47. }  
  48. static void info_init (PrivInfo* thiz)  
  49. {  
  50.     return_if_fail (thiz != NULL);  
  51.     thiz->end_time = time(NULL) + 10;  
  52.     sem_init (&thiz->s1, 0, 1);  
  53.     sem_init (&thiz->s2, 0, 0);  
  54.     return;  
  55. }  
  56. static void info_destroy (PrivInfo* thiz)  
  57. {  
  58.     return_if_fail (thiz != NULL);  
  59.     sem_destroy (&thiz->s1);  
  60.     sem_destroy (&thiz->s2);  
  61.     free (thiz);  
  62.     thiz = NULL;  
  63.     return;  
  64. }  
  65. static void* pthread_func_1 (PrivInfo* thiz)  
  66. {  
  67.     return_if_fail(thiz != NULL);  
  68.     while (time(NULL) < thiz->end_time)  
  69.     {  
  70.         sem_wait (&thiz->s2);  
  71.         printf ("pthread1: pthread1 get the lock./n");  
  72.         sem_post (&thiz->s1);  
  73.         printf ("pthread1: pthread1 unlock/n");  
  74.         sleep (1);  
  75.     }  
  76.     return;  
  77. }  
  78. static void* pthread_func_2 (PrivInfo* thiz)  
  79. {  
  80.     return_if_fail (thiz != NULL);  
  81.     while (time (NULL) < thiz->end_time)  
  82.     {  
  83.         sem_wait (&thiz->s1);  
  84.         printf ("pthread2: pthread2 get the unlock./n");  
  85.         sem_post (&thiz->s2);  
  86.         printf ("pthread2: pthread2 unlock./n");  
  87.         sleep (1);  
  88.     }  
  89.     return;  
  90. }  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/384218.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Elixir特性

iex 退出&#xff1a;Ctrl-C 或Ctrl-G再输入q 回车。 帮助文档&#xff1a;h 查看辅函数列表 h IO 查看IO模块帮助 h IO.puts 查看IO模块中的puts函数的文档 编译和运行&#xff1a;创建一个hello.exs的文件。IO.puts "hello world"    //输出hello world 使用el…

Elixir基础

值类型 整数&#xff0c;包括十进制&#xff08;1234&#xff09;、十六进制&#xff08;0xcafe&#xff09;、八进制&#xff08;0o765&#xff09;和二进制&#xff08;0b1010&#xff09; 浮点数 原子&#xff0c;原子是常量&#xff0c;用于表现某些东西的名字&#xff0c;…

C++11新特性之八——函数对象function

http://www.cnblogs.com/yyxt/p/3987717.html 详细请看《C Primer plus》(第六版中文版) http://www.cnblogs.com/lvpengms/archive/2011/02/21/1960078.html 备注&#xff1a; 函数对象&#xff1a; 尽管函数指针被广泛用于实现函数回调&#xff0c;但C还提供了一个重要的实现…

分块思想

今天学习了一个算法&#xff08;这个应该叫做算法吧&#xff1f;&#xff09;叫做分块&#xff08;和莫队&#xff0c;但是莫队还没有搞懂&#xff0c;搞懂再来写吧&#xff09; 听起来很高级&#xff0c;蒟蒻表示瑟瑟发抖。但是学完发现怎么那么像是一种变相的暴力呢。 分块思…

从零开始学C++之STL(八):函数对象、 函数对象与容器、函数对象与算法

http://blog.csdn.net/jnu_simba/article/details/9500219 一、函数对象 1、函数对象&#xff08;function object&#xff09;也称为仿函数&#xff08;functor&#xff09; 2、一个行为类似函数的对象&#xff0c;它可以没有参数&#xff0c;也可以带有若干参数。 3、任何重载…

树状数组初步理解

学习树状数组已经两周了&#xff0c;之前偷懒一直没有写&#xff0c;赶紧补上防止自己忘记&#xff08;虽然好像已经忘得差不多了&#xff09;。 作为一种经常处理区间问题的数据结构&#xff0c;它和线段树、分块一样&#xff0c;核心就是将区间分成许多个小区间然后通过对大区…

命名函数

函数体是代码块 代码块do...end是一种表达式的组织方式。 # ./times.exs下defmodule Times dodef doule(n) don * 2end end 函数调用与模式匹配 代码如下&#xff1a; # ./factorial.exs    计算阶层 defmodule Factorial dodef of(0), do: 1          #终止条件…

STL运用的C++技术(6)——函数对象

http://blog.csdn.net/wuzhekai1985/article/details/6658940?_t_t_t0.20427969420870595 STL是C标准库的重要组成部分之一&#xff0c;它不仅是一个可复用的组件库&#xff0c;更是一个包含算法与数据结构的软件框架&#xff0c;同时也是C泛型编程的很好例子。STL中运用了许多…

列表与递归

头部和尾部 [head | tail ] [1] #head 1 tail [] [head | tail ] [1, 2, 3] #head 1 tail [2, 3] [head | tail ] [] #报错 创建映射函数 我们可以使用一个函数来处理列表中的各个元素&#xff0c;如此可以接受更加复杂的处理&#xff0c;也可以…

优先队列小结

不像栈和队列&#xff0c;虽然STL有较好实现但是我们自己也可以很方便的实现&#xff0c;优先队列自己实现起来就比较复杂&#xff0c;比较浪费时间&#xff08;而且自己目前也不会233&#xff09;而优先队列因为其较好的特性经常被使用&#xff0c;因此对它的熟练掌握是做题的…

字典:散列表、散列字典、关键字列表、集合与结构体

字典 散列表和散列字典都实现了Dict的行为。Keyword模块也基本实现了&#xff0c;不同之处在于它支持重复键。 Eunm.into可以将一种类型的收集映射转化成另一种。 defmodule Sum dodef values(dict) dodict |> Dict.values |> Enum.sumend endhd [ one: 1, two: 2, thre…

C++11 学习笔记 lambda表达式

http://blog.csdn.net/fjzpdkf/article/details/50249287 lambda表达式是C11最重要也最常用的一个特性之一。lambda来源于函数式编程的概念&#xff0c;也是现代编程语言的一个特点。 一.函数式编程简介 定义&#xff1a;简单说&#xff0c;“函数式编程”是一种“编程范式”。…

Cutting Codeforces Round #493 (Div. 2)

Cutting There are a lot of things which could be cut — trees, paper, “the rope”. In this problem you are going to cut a sequence of integers. There is a sequence of integers, which contains the equal number of even and odd numbers. Given a limited bud…

Enum、Stream

Enum 其常见用法见&#xff1a;https://cloud.tencent.com/developer/section/1116852 在sort时&#xff0c;如果要获得稳定的排序结果&#xff0c;要使用< 而不是 <。 Stream Stream是延迟处理的&#xff0c;而Enum是贪婪的&#xff0c;则意味着传给它一个收集&#xff…

linux网络编程之posix 线程(三):posix 匿名信号量与互斥锁 示例生产者--消费者问题

http://blog.csdn.net/jnu_simba/article/details/9123603 一、posix 信号量 信号量的概念参见这里。前面也讲过system v 信号量&#xff0c;现在来说说posix 信号量。 system v 信号量只能用于进程间同步&#xff0c;而posix 信号量除了可以进程间同步&#xff0c;还可以线程间…

洛谷P1080-国王游戏-贪心+高精度

P1080-国王游戏 啊啊啊&#xff0c;刚才已经写了一次了&#xff0c;但是Edge浏览器不知道为什么卡住了&#xff0c;难受。 好吧&#xff0c;其实是一道可做题&#xff0c;分析得到的贪心策略就是就是将a * b小的放在前面&#xff08;其他的懒得说了&#xff09;&#xff0c;主要…

字符串与二进制

单引号字符串会被表示成整数值列表。 &#xff1f;c返回字符 c 的整数编码。下面这个例子用于解析字符列表表示法&#xff0c;该表示法用于表示一个任意的有符号的十进制数据。 defmodule Parse dodef number([ ?- | tail ]) do_number_digits(tail, 0) * -1enddef number([ ?…

P1092虫食算-深度优先搜索+玄学剪枝

P1092虫食算 这道题的思想并不复杂&#xff0c;可是难点在于各种玄学剪枝。在仔细研究了题解大佬的剪枝原理后终于氵了过去。 先上代码&#xff1a; #include<cstdio> #include<cstring> #include<algorithm> using namespace std;const int MAXN100; int n…

多进程

使用spawn创建一个新进程&#xff0c;其第一个参数是模块名、第二个参数是函数名、第三个参数是参数列表。spawn会返回一个进程标识符&#xff0c;通常叫做PID。 defmodule Spawn1 dodef greet doreceive do{sender, msg} ->send sender, { :ok, "Hello #{msg}" }…

Linux socket编程(二) 服务器与客户端的通信

http://www.cnblogs.com/-Lei/archive/2012/09/04/2670964.html上一篇写了对套接字操作的封装&#xff0c;这一节使用已封装好的Socket类实现服务器与客户端的通信&#xff08;Socket的定义见上篇Socket.h) 服务器端&#xff1a; ServerSocket.h #ifndef SERVERSOCKET_H #defin…