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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

无锁队列设计思路以及简要代码

發(fā)布時(shí)間:2023/12/1 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 无锁队列设计思路以及简要代码 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • 非并發(fā)的一寫一讀環(huán)形隊(duì)列
  • 多讀多寫環(huán)形隊(duì)列

非并發(fā)的一寫一讀環(huán)形隊(duì)列

讀指針:
1、先判斷是否有數(shù)據(jù)
2、讀取數(shù)據(jù)
3、操作指針
寫指針:
1、先判斷空間是否足夠
2、寫入數(shù)據(jù)
3、操作指針·
所以代碼也十分簡(jiǎn)單:

bool putqueue(void* pData) {ST_NODE* ptr = NULL;do {ptr = pWrite;if ((uiQueueSize - ((pWrite + uiQueueSize - pRead) % uiQueueSize) - 1 > 0) || (NULL= *ptr)) // 隊(duì)列滿了 {return false;}} while(!_sync_bool_compare_and_swap(pWrite, ptr, ptr + 1)) // 競(jìng)爭(zhēng)到寫指針if (pWrite >= pstBegin) {pWrite = pstBegin;}*ptr = pData;return true; }

那么在多線程多讀多寫的情況下,該如何設(shè)計(jì)呢?

多讀多寫環(huán)形隊(duì)列

核心問題是:
1、多個(gè)線程如何競(jìng)爭(zhēng)操作一個(gè)指針?
思路:利用CAS(compare & swap)確保只有一個(gè)線程能把指針從當(dāng)前位置指向下一個(gè)位置
2、先操作指針還是先操作數(shù)據(jù)?

  • 先操作指針,有可能導(dǎo)致數(shù)據(jù)還沒讀,就被寫入方覆蓋
  • 先讀/寫數(shù)據(jù),可能無法競(jìng)爭(zhēng)到指針導(dǎo)致錯(cuò)誤
  • 解決方案:標(biāo)記法,已讀取得數(shù)據(jù)置為NULL,未讀數(shù)據(jù)為實(shí)際數(shù)據(jù)得指針,讀寫前先判斷標(biāo)記。
void* getqueue() {ST_NODE* ptr = NULL;ST_NODE* current = NULL;do {ptr = pRead;if (((pWrite + uiQueueSize - pRead) % uiQueueSize) > 0 || (NULL= ptr)) // 隊(duì)列空{return NULL;}} while(!_sync_bool_compare_and_swap(pRead, ptr, ptr + 1)) // 競(jìng)爭(zhēng)到讀指針if (pRead >= pstEnd) {pRead = pstEnd;}current = *ptr;*ptr = NULL;return *current; }

此時(shí)也會(huì)出現(xiàn)一些極端的問題:
1、CAS指令的ABA問題:兩個(gè)線程同時(shí)讀/寫同一個(gè)位置,第一個(gè)線程獲取讀/寫權(quán)限后,第二個(gè)線程掛起。
指針有可能轉(zhuǎn)一圈回到原來位置,導(dǎo)致第二個(gè)線程恢復(fù)運(yùn)行,從而第二個(gè)線程CAS成功。極端情況下會(huì)導(dǎo)致讀指針越過寫指針。
解決方案:通過一個(gè)唯一id:seq替換指針,seq為64位整數(shù),自增且永不重復(fù)。指針 = 隊(duì)列首地址 + seq % 隊(duì)列長(zhǎng)度

class mqueue { public:mqueue() {read_seq_ = write_seq_ = 0;memset(queue_arr_, 0, sizeof(queue_arr_));}bool push_back(void* data); // 插入元素void* pop_front(); // 取出元素 private:void* queue_arr_[MAXN];volatile uint64_t read_seq_;volatile uint64_t write_seq_; };bool mqueue::push_back(void* data) {do {uint64_t cur_seq = write_seq_; // 保留原始值,避免處理過程被其他線程改變if (cur_seq >= read_seq_ + MAXN || queue_arr_[cur_seq % MAXN]){return false; // 隊(duì)列滿了,等讀線程讀取}if (_sync_bool_compare_and_swap(&write_seq_, cur_seq, cur_seq + 1)){queue_arr_[cur_seq % MAXN] = data;return true;}} while (true); }void* mqueue::pop_front() {do{uint64_t cur_seq = read_seq_; // 保留原始值,避免處理過程被其他線程改變if (cur_seq >= write_seq_ || queue_arr_[cur_seq % MAXN] == NULL){return NULL; // 隊(duì)列空,等待寫線程寫入 }if (_sync_bool_compare_and_swap(&read_seq_, cur_seq, cur_seq + 1)){void* data = queue_arr_[cur_seq % MAXN];queue_arr_[cur_seq % MAXN] = NULL;return data;}} while (true); }

總結(jié)

以上是生活随笔為你收集整理的无锁队列设计思路以及简要代码的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。