日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

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

發布時間:2023/11/30 linux 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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》


總結

以上是生活随笔為你收集整理的linux网络编程之posix 线程(三):posix 匿名信号量与互斥锁 示例生产者--消费者问题的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。