Linux线程(五)
生活随笔
收集整理的這篇文章主要介紹了
Linux线程(五)
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
Linux線程(五)
文章目錄
- Linux線程(五)
- 一、posix信號量
- 二:生產(chǎn)者消費(fèi)者模型
- 三、讀者寫鎖問題
一、posix信號量
-
1.相關(guān)概念:
-
posix信號量和system V信號量作用一樣,都是用于同步操作,達(dá)到無沖突訪問共享資源的目的。但Posix可以用于線程間同步
-
2.sem_init函數(shù)
- 功能:初始化信號量
- 參數(shù):
- 3.sem_destroy函數(shù)
-
作用:銷毀一個信號量
-
4.sem_post函數(shù)
- 功能:發(fā)布信號量,表示資源使用完畢,可以歸還資源了。將信號量的值 + 1
- 5.sem_wait函數(shù)
- 功能:等待信號量,會將信號量的值 — 1;
二:生產(chǎn)者消費(fèi)者模型
上次生產(chǎn)者消費(fèi)者模型是基于queue實現(xiàn)的,其空間是可以動態(tài)分配的,現(xiàn)在基于固定大小的環(huán)形隊列重寫這個代碼
#include <iostream> #include <vector> #include <stdlib.h> #include <semaphore.h> #include <pthread.h> #define NUM 16class RingQueue{ private:std::vector<int> q;int cap;sem_t data_sem;sem_t space_sem;int consume_step;int product_step; public:RingQueue(int _cap = NUM):q(_cap),cap(_cap){sem_init(&data_sem, 0, 0);sem_init(&space_sem, 0, cap);consume_step = 0;product_step = 0;} void PutData(const int &data){sem_wait(&space_sem); // Pq[consume_step] = data;consume_step++;consume_step %= cap;sem_post(&data_sem); //V}void GetData(int &data){sem_wait(&data_sem);data = q[product_step];product_step++;product_step %= cap;sem_post(&space_sem);} ~RingQueue(){sem_destroy(&data_sem);sem_destroy(&space_sem);} };void *consumer(void *arg) {RingQueue *rqp = (RingQueue*)arg;int data;for( ; ; ){rqp->GetData(data);std::cout << "Consume data done : " << data << std::endl;sleep(1);} } //more faster void *producter(void *arg) {RingQueue *rqp = (RingQueue*)arg;srand((unsigned long)time(NULL));for( ; ; ){int data = rand() % 1024;rqp->PutData(data);std::cout << "Prodoct data done: " << data << std::endl;// sleep(1);} } int main() {RingQueue rq;pthread_t c,p;pthread_create(&c, NULL, consumer, (void*)&rq);pthread_create(&p, NULL, producter, (void*)&rq);pthread_join(c, NULL);pthread_join(p, NULL);return 0; }三、讀者寫鎖問題
- 在編寫多線程的時候,有一種情況是十分常見的。那就是,有些公共數(shù)據(jù)修改的機(jī)會比較少。相比較改寫,它們讀的機(jī)會反而高的多。
- 通常而言,在讀的過程中,往往伴隨著查找的操作,中間耗時很長。給這種代碼段加鎖,會極大地降低我們程序的效率。
- 那么有沒有一種方法,可以專門處理這種多讀少寫的情況呢? 有,那就是讀寫鎖
- 1.pthread_rwlock_init函數(shù)
- 功能:初始化讀寫鎖
- 參數(shù)1:初始化的讀寫鎖
- 參數(shù)2:讀寫鎖初始化時的屬性。如果用默認(rèn)屬性,此處填NULL
- 2.pthread_rwlock_destory函數(shù)
- 功能:銷毀一個讀寫鎖
- 如果在pthread_rwlock_destory之前就釋放了讀寫鎖占用的內(nèi)存空間,那么分配給這個鎖的資源就會丟失
- 備注(重點):此函數(shù)只是反初始化讀寫鎖變量,并沒有釋放內(nèi)存空間,如果讀寫鎖變量是通過malloc等函數(shù)申請的,那么需要在free掉讀寫鎖變量之前調(diào)用pthread_rwlock_destory函數(shù)
- 3.pthread_rwlock_rdlock函數(shù)
- 功能:在讀模式下鎖定讀寫鎖
- 4.pthread_rwlock_wrlock函數(shù)
- 功能:在寫模式下鎖定讀寫鎖
- 5.pthread_rwlock_unlock函數(shù)
- 不管以何種方式鎖住讀寫鎖,都可以用這個函數(shù)解鎖
- 各種實現(xiàn)可能會對共享模式下可獲取的讀寫鎖的次數(shù)進(jìn)行限制,所以需要檢查pthread_rwlock_rdlock的返回值
- 即使pthread_rwlock_wrlock和pthread_rwlock_unlock有錯誤返回,而且從技術(shù)上來講,在調(diào)用函數(shù)時應(yīng)該總會檢查錯誤返回,但是如果鎖設(shè)計合理的話,就不需要檢查它們。
- 錯誤返回值的定義只是針對不正確使用讀寫鎖的情況(如未經(jīng)初始化的鎖),或者試圖獲取已擁有的鎖從而可能產(chǎn)生死鎖的情況
- 但是還需要注意,有些特定的實現(xiàn)可能會定義另外的錯誤返回
- 6.設(shè)置讀寫鎖優(yōu)先
- pref 共有 3 種選擇
測試代碼:
#include <vector> #include <sstream> #include <cstdio> #include <cstdlib> #include <cstring> #include <unistd.h> #include <pthread.h>volatile int ticket = 1000; pthread_rwlock_t rwlock; void * reader(void * arg) {char *id = (char *)arg;while (1) {pthread_rwlock_rdlock(&rwlock);if (ticket <= 0) {pthread_rwlock_unlock(&rwlock);break;}printf("%s: %d\n", id, ticket);pthread_rwlock_unlock(&rwlock);usleep(1);} return nullptr; }void * writer(void * arg) {char *id = (char *)arg;while (1) {pthread_rwlock_wrlock(&rwlock);if (ticket <= 0) {pthread_rwlock_unlock(&rwlock);break;} printf("%s: %d\n", id, --ticket);pthread_rwlock_unlock(&rwlock);usleep(1);}return nullptr; }struct ThreadAttr {pthread_t tid;std::string id; };std::string create_reader_id(std::size_t i) {// 利用 ostringstream 進(jìn)行 string 拼接std::ostringstream oss("thread reader ", std::ios_base::ate); oss << i;return oss.str(); } std::string create_writer_id(std::size_t i) {// 利用 ostringstream 進(jìn)行 string 拼接std::ostringstream oss("thread writer ", std::ios_base::ate);oss << i;return oss.str(); }void init_readers(std::vector<ThreadAttr>& vec) {for (std::size_t i = 0; i < vec.size(); ++i) {vec[i].id = create_reader_id(i);pthread_create(&vec[i].tid, nullptr, reader, (void *)vec[i].id.c_str());} }void init_writers(std::vector<ThreadAttr>& vec) {for (std::size_t i = 0; i < vec.size(); ++i) {vec[i].id = create_writer_id(i);pthread_create(&vec[i].tid, nullptr, writer, (void *)vec[i].id.c_str());} } void join_threads(std::vector<ThreadAttr> const& vec) {// 我們按創(chuàng)建的 逆序 來進(jìn)行線程的回收for (std::vector<ThreadAttr>::const_reverse_iterator it = vec.rbegin(); it !=vec.rend(); ++it) {pthread_t const& tid = it->tid;pthread_join(tid, nullptr);} }void init_rwlock() { #if 0 // 寫優(yōu)先pthread_rwlockattr_t attr;pthread_rwlockattr_init(&attr);pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);pthread_rwlock_init(&rwlock, &attr);pthread_rwlockattr_destroy(&attr);#else // 讀優(yōu)先,會造成寫?zhàn)囸Ipthread_rwlock_init(&rwlock, nullptr);#endif } int main() {// 測試效果不明顯的情況下,可以加大 reader_nr// 但也不能太大,超過一定閾值后系統(tǒng)就調(diào)度不了主線程了const std::size_t reader_nr = 1000;const std::size_t writer_nr = 2;std::vector<ThreadAttr> readers(reader_nr);std::vector<ThreadAttr> writers(writer_nr);init_rwlock();init_readers(readers);init_writers(writers);join_threads(writers);join_threads(readers);pthread_rwlock_destroy(&rwlock); }總結(jié)
以上是生活随笔為你收集整理的Linux线程(五)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux线程(四)
- 下一篇: Linux线程(六)