linux網絡編程之posix 線程(三):posix 匿名信號量與互斥鎖 示例生產者--消費者問題

http://blog.csdn.net/jnu_simba/article/details/9123603

一、posix 信號量

信號量的概念參見這里前面也講過system v 信號量,現在來說說posix 信號量。

system v 信號量只能用于進程間同步,而posix 信號量除了可以進程間同步,還可以線程間同步。system v 信號量每次PV操作可以是N,但Posix 信號量每次PV只能是1。除此之外,posix 信號量還有命名和匿名之分(man 7 sem_overview):

1、命名信號量

名字以/somename 形式分辨,只能有一個/ ,且總長不能超過NAME_MAX - 4(一般是251)。

需要用sem_open 函數創建或打開,PV操作分別是sem_wait 和 sem_post,可以使用sem_close 關閉,刪除用sem_unlink。

命名信號量用于不共享內存的進程間同步(內核實現),類似system v 信號量。

2、匿名信號量

存放在一塊共享內存中,如果是線程共享,這塊區域可以是全局變量;如果是進程共享,可以是system v 共享內存(shmget 創建,shmat 映射),也可以是 posix 共享內存(shm_open 創建,mmap 映射)。

匿名信號量必須用sem_init 初始化,sem_init 函數其中一個參數pshared決定了線程共享還是進程共享,也可以用sem_post 和sem_wait 進行操作,在共享內存釋放前,匿名信號量要先用sem_destroy 銷毀。

有關這些函數的具體參數可以man 一下。


二、互斥鎖

對于多線程的程序,訪問沖突的問題是很普遍的,解決的辦法是引入互斥鎖(Mutex,MutualExclusive Lock),獲得鎖的線程可以完成“讀-修改-寫”的操作,然后釋放鎖給其它線程,沒有獲得鎖的線程只能等待而不能訪問共享數據,這樣“讀-修改-寫”三步操作組成一個原子操作,要么都執行,要么都不執行,不會執行到中間被打斷,也不會在其它處理器上并行做這個操作。

Mutex用pthread_mutex_t類型的變量表示,pthread_mutex_init函數對Mutex做初始化,參數attr設定Mutex的屬性,如果attr為NULL則表示缺省屬性,具體看結構體:有人建議開發中總是設置 PTHREAD_MUTEX_RECURSIVE 屬性,避免死鎖。

// 互斥量屬性: 同一線程可多次加鎖
pthread_mutexattr_t m_attr;
pthread_mutexattr_init(&m_attr);
pthread_mutexattr_settype(&m_attr, PTHREAD_MUTEX_RECURSIVE);

C++ Code?
1
2
3
4
5
6
7
8
9
10
struct?pthread_mutexattr_t
{
????enum?lock_type????//?使用pthread_mutexattr_settype來更改
????{
?????????PTHREAD_MUTEX_TIMED_NP?[default]//當一個線程加鎖后,其余請求鎖的線程形成等待隊列,在解鎖后按優先級獲得鎖。
?????????PTHREAD_MUTEX_ADAPTIVE_NP???????//?動作最簡單的鎖類型,解鎖后所有線程重新競爭。
?????????PTHREAD_MUTEX_RECURSIVE_NP??????//?允許同一線程對同一鎖成功獲得多次。當然也要解鎖多次。其余線程在解鎖時重新競爭。
?????????PTHREAD_MUTEX_ERRORCHECK_NP?????//?若同一線程請求同一鎖,返回EDEADLK,否則與PTHREAD_MUTEX_TIMED_NP動作相同。
????}?type;
}?attr;

用pthread_mutex_init函數初始化的Mutex可以用pthread_mutex_destroy銷毀。如果Mutex變量是靜態分配的(全局變量或static變量),也可以用宏定義PTHREAD_MUTEX_INITIALIZER來初始化,相當于用pthread_mutex_init初始化并且attr參數為NULL。

一個線程可以調用pthread_mutex_lock獲得Mutex,如果這時另一個線程已經調用pthread_mutex_lock獲得了該Mutex,則當前線程需要掛起等待,直到另一個線程調用pthread_mutex_unlock釋放Mutex,當前線程被喚醒,才能獲得該Mutex并繼續執行。

如果一個線程既想獲得鎖,又不想掛起等待,可以調用pthread_mutex_trylock,如果Mutex已經被另一個線程獲得,這個函數會失敗返回EBUSY,而不會使線程掛起等待。

上面的具體函數可以man 一下。


三、生產者消費者問題

生產者消費者問題概念參見這里。下面使用posix 信號量和互斥鎖一起來演示:

C++ Code?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include?<unistd.h>
#include?<sys/types.h>
#include?<pthread.h>
#include?<semaphore.h>

#include?<stdlib.h>
#include?<stdio.h>
#include?<errno.h>
#include?<string.h>

#define?ERR_EXIT(m)?\
????????do?\
????????{?\
????????????????perror(m);?\
????????????????exit(EXIT_FAILURE);?\
????????}?while(0)

#define?CONSUMERS_COUNT?1
#define?PRODUCERS_COUNT?1
#define?BUFFSIZE?10

int?g_buffer[BUFFSIZE];

unsigned?short?in?=?0;
unsigned?short?out?=?0;
unsigned?short?produce_id?=?0;
unsigned?short?consume_id?=?0;

sem_t?g_sem_full;
sem_t?g_sem_empty;
pthread_mutex_t?g_mutex;

pthread_t?g_thread[CONSUMERS_COUNT?+?PRODUCERS_COUNT];

void?*consume(void?*arg)
{
????int?i;
????int?num?=?(int)arg;
????while?(1)
????{
????????printf("%d?wait?buffer?not?empty\n",?num);
????????sem_wait(&g_sem_empty);
????????pthread_mutex_lock(&g_mutex);

????????for?(i?=?0;?i?<?BUFFSIZE;?i++)
????????{
????????????printf("%02d?",?i);
????????????if?(g_buffer[i]?==?-1)
????????????????printf("%s",?"null");
????????????else
????????????????printf("%d",?g_buffer[i]);

????????????if?(i?==?out)
????????????????printf("\t<--consume");

????????????printf("\n");
????????}
????????consume_id?=?g_buffer[out];
????????printf("%d?begin?consume?product?%d\n",?num,?consume_id);
????????g_buffer[out]?=?-1;
????????out?=?(out?+?1)?%?BUFFSIZE;
????????printf("%d?end?consume?product?%d\n",?num,?consume_id);
????????pthread_mutex_unlock(&g_mutex);
????????sem_post(&g_sem_full);
????????sleep(1);
????}
????return?NULL;
}

void?*produce(void?*arg)
{
????int?num?=?(int)arg;
????int?i;
????while?(1)
????{
????????printf("%d?wait?buffer?not?full\n",?num);
????????sem_wait(&g_sem_full);
????????pthread_mutex_lock(&g_mutex);
????????for?(i?=?0;?i?<?BUFFSIZE;?i++)
????????{
????????????printf("%02d?",?i);
????????????if?(g_buffer[i]?==?-1)
????????????????printf("%s",?"null");
????????????else
????????????????printf("%d",?g_buffer[i]);

????????????if?(i?==?in)
????????????????printf("\t<--produce");

????????????printf("\n");
????????}

????????printf("%d?begin?produce?product?%d\n",?num,?produce_id);
????????g_buffer[in]?=?produce_id;
????????in?=?(in?+?1)?%?BUFFSIZE;
????????printf("%d?end?produce?product?%d\n",?num,?produce_id++);
????????pthread_mutex_unlock(&g_mutex);
????????sem_post(&g_sem_empty);
????????sleep(5);
????}
????return?NULL;
}

int?main(void)
{
????int?i;
????for?(i?=?0;?i?<?BUFFSIZE;?i++)
????????g_buffer[i]?=?-1;

????sem_init(&g_sem_full,?0,?BUFFSIZE);
????sem_init(&g_sem_empty,?0,?0);

????pthread_mutex_init(&g_mutex,?NULL);


????for?(i?=?0;?i?<?CONSUMERS_COUNT;?i++)
????????pthread_create(&g_thread[i],?NULL,?consume,?(void?*)i);

????for?(i?=?0;?i?<?PRODUCERS_COUNT;?i++)
????????pthread_create(&g_thread[CONSUMERS_COUNT?+?i],?NULL,?produce,?(void?*)i);

????for?(i?=?0;?i?<?CONSUMERS_COUNT?+?PRODUCERS_COUNT;?i++)
????????pthread_join(g_thread[i],?NULL);

????sem_destroy(&g_sem_full);
????sem_destroy(&g_sem_empty);
????pthread_mutex_destroy(&g_mutex);

????return?0;
}

與這里的程序相比,程序邏輯沒太大變化,只是用pthread_mutex_lock 替代了 sem_mutex,其次這里是演示線程間同步,現在上述程序生產者消費者各一個線程,但生產者睡眠時間是消費者的5倍,故消費者會經常阻塞在sem_wait(&g_sem_empty) 上面,因為緩沖區經常為空,可以將PRODUCTORS_COUNT 改成5,即有5個生產者線程和1個消費者線程,而且生產者睡眠時間還是消費者的5倍,從動態輸出可以看出,基本上就動態平衡了,即5個生產者一下子生產了5份東西,消費者1s消費1份,剛好在生產者繼續生產前消費完。


四、自旋鎖和讀寫鎖簡介

(一)、自旋鎖

自旋鎖類似于互斥鎖,它的性能比互斥鎖更高。
自旋鎖與互斥鎖很重要的一個區別在于,線程在申請自旋鎖的時候,線程不會被掛起,它處于忙等待的狀態,一般用于等待時間比較短的情形。
pthread_spin_init
pthread_spin_destroy
pthread_spin_lock
pthread_spin_unlock

(二)、讀寫鎖

1、只要沒有線程持有給定的讀寫鎖用于寫,那么任意數目的線程可以持有讀寫鎖用于讀
2、僅當沒有線程持有某個給定的讀寫鎖用于讀或用于寫時,才能分配讀寫鎖用于寫
3、讀寫鎖用于讀稱為共享鎖,讀寫鎖用于寫稱為排它鎖
pthread_rwlock_init
pthread_rwlock_destroy
int pthread_rwlock_rdlock
int pthread_rwlock_wrlock
int pthread_rwlock_unlock


更多有關linux中的鎖問題可以參考這篇文章 :《透過Linux內核看無鎖編程》

http://www.ibm.com/developerworks/cn/linux/l-cn-lockfree/


參考:

《linux c 編程一站式學習》

《UNP》


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/384203.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/384203.shtml
英文地址,請注明出處:http://en.pswp.cn/news/384203.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

洛谷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…

OTP服務器

defmodule Sequence.Server douse GenServerdef handle_call( :next_number, _from, current_number) do{ :reply, current_number, current_number 1}  #reply告訴OTP將第二個元素返回給客戶端end end use的效果將OTP GenServer的行為添加到當前模塊。這樣它就可以處理所有…

洛谷P1040-加分二叉樹-dp+二叉樹

P1040-加分二叉樹 這道題放在深度優先搜索的訓練題中&#xff0c;可是我實在沒有看出來應該怎么搜索。看了題解以后才看出來是一個很簡單的dp(我果然還是太菜了) 看出dp并且算出來最大的分數不是很復雜&#xff0c;關鍵是輸出給定中序遍歷序列的二叉樹的先序遍歷&#xff0c;要…

UNIX網絡編程:I/O復用技術(select、poll、epoll)

http://blog.csdn.net/dandelion_gong/article/details/51673085 Unix下可用的I/O模型一共有五種&#xff1a;阻塞I/O 、非阻塞I/O 、I/O復用 、信號驅動I/O 、異步I/O。此處我們主要介紹第三種I/O符復用。 I/O復用的功能&#xff1a;如果一個或多個I/O條件滿足&#xff08;輸…

解決iex -S mix報錯

執行iex -S mix命令的時候會遇到如下錯誤&#xff1a; 執行 mix deps.get 然后就可以運行 iex -S mix了 其中&#xff0c;有可能會出現 按照其網站下載相應文件&#xff0c;復制到項目根目錄下&#xff0c;然后執行命令&#xff08;mix local.rebar rebar ./rebar&#xff09;即…

貪心算法——選擇不相交區間問題

題目描述&#xff1a;設有n個活動的集合&#xff0c;其中每個活動都要求使用同一個資源&#xff0c;而在同一時間內只有一個活動能夠使用這一資源&#xff0c;每個活動i都有一個要求使用該資源的起始時間si和一個結束時間fi(si<fi)&#xff0c;如果選擇了活動i&#xff0c;則…

Anker—工作學習筆記

http://www.cnblogs.com/Anker/archive/2013/08/17/3263780.html 1、基本知識 epoll是在2.6內核中提出的&#xff0c;是之前的select和poll的增強版本。相對于select和poll來說&#xff0c;epoll更加靈活&#xff0c;沒有描述符限制。epoll使用一個文件描述符管理多個描述符&am…

Supervisor監控

可參考&#xff1a;https://www.cnblogs.com/wang_yb/archive/2016/06/08/5564459.html &#xff1a;https://segmentfault.com/a/1190000007379204 轉載于:https://www.cnblogs.com/lr1402585172/p/11551488.html

深度搜索剪枝——數的劃分

【題目描述】將整數n分成k份&#xff0c;且每份不能為空&#xff0c;問有多少種分法&#xff1f; 【輸入格式】兩個整數n,m(6<n<200,2<m<6) 【輸出格式】輸出不同的分法數 【樣例輸入】7 3 【樣例輸出】4 對于這種搜索題&#xff0c;關鍵就在于剪枝&#xff1a;確定…

Linux網絡編程——tcp并發服務器(I/O復用之select

http://blog.csdn.net/lianghe_work/article/details/46519633 與多線程、多進程相比&#xff0c;I/O復用最大的優勢是系統開銷小&#xff0c;系統不需要建立新的進程或者線程&#xff0c;也不必維護這些線程和進程。 代碼示例&#xff1a; [csharp] view plaincopy #include &…

ets

:ets.new(table_name, pattern) 第一個參數是表名&#xff0c;第二個參數是表的設置選項。 :set  一個key&#xff0c;一個數據&#xff0c;無序 :ordered_set  一個key&#xff0c;一個數據&#xff0c;有序&#xff1b; 1 1.0 :bag  一個key&#xff0c;多個數據&…

貪心算法-區間選點問題-種樹

【題目描述】一條街道的一邊有幾座房子。因為環保原因居民想要在路邊種些樹&#xff0c;路邊的地區被分割成n塊&#xff0c;并被編號為1~n。每塊大小為一個單位尺寸且最多可總一棵樹。每個居民想在門前種些樹并制定了三個數b,e,t&#xff0c;這三個數代表居民想在b和e之間最少種…

ets注意事項

當表類型為 :set 時&#xff0c;使用 :ets.first 和 :ets.last 會獲取到同一個 key。將表類型換為 :oedered_set 就可以避免這種情況 轉載于:https://www.cnblogs.com/lr1402585172/p/11599219.html

CodeForces - 1141CPolycarp Restores Permutation搜索+剪枝

Polycarp Restores Permutation 【題意分析】題意大概是給定一個串&#xff0c;包含從1到n所有的數字。但是給定的是相鄰數字的差&#xff0c;需要復原這個串。 大概分析以后發現給定的是一個差分數組&#xff0c;所以只需要枚舉第一個元素就可以確定所有元素的值。 問題是如何…

CodeForces - 1141ESuperhero Battle簡單模擬

Superhero Battle 這道題卡了我一個多小時&#xff0c;最后也沒有做出來&#xff0c;成功稱為吊車尾。。。 思路什么的都沒有問題&#xff0c;主要是&#xff0c;爆long long了&#xff0c;這個太可怕了&#xff0c;就因為一個中間變量忘記開longlong導致一直一直wa&#xff0c…

Linux下的I/O復用與epoll詳解

http://www.cnblogs.com/lojunren/p/3856290.html 前言 I/O多路復用有很多種實現。在linux上&#xff0c;2.4內核前主要是select和poll&#xff0c;自Linux 2.6內核正式引入epoll以來&#xff0c;epoll已經成為了目前實現高性能網絡服務器的必備技術。盡管他們的使用方法不盡相…