1. 程式人生 > >多執行緒模型下的無鎖程式設計

多執行緒模型下的無鎖程式設計

多執行緒模式是比較流行的一種併發程式設計模型,多執行緒程式設計的一個特點就是執行緒間共享記憶體空間;這可以降低執行緒間通訊的開銷,但卻引來了另外的一個難纏的問題:竟態條件!,因此,甚至有人對多執行緒模型提出了質疑,看這裡

在多執行緒程式設計模型下,解決竟態條件的傳統方法就是加鎖保護臨界區,但這存在影響系統性能、優先順序反轉等問題.

因此又有人提出了,多執行緒模型下無鎖程式設計的一些方式:
1.執行緒內通訊框架: Disruptor, 這是一款開源的併發框架,用於執行緒間無鎖的共享資料,看這裡

2.無鎖資料結構
無鎖資料結構一般基於一個很重要的操作:CAS--Compare And Swap(看這裡)。

用C語言表達的一個CAS實現的操作是這樣的:

  1. /*定義CAS操作*/
  2. #define CAS __sync_bool_compare_and_swap
  3. /*
  4. * 定義stack的資料結構
  5. */
  6. typedef struct stack_node {
  7.     struct node *next;
  8.     void *data;
  9. }stack_node;
  10. /*棧頂指標*/
  11. stack_node *top = NULL;

CAS的一個重要特性是其必須是原子操作。現在大多數CPU都支援指令級別的CAS操作。GCC編譯也提供了這樣的介面:bool __sync_bool_compare_and_swap (type *ptr, type oldval type newval, ...)


有了這個原子的CAS後,我們就能實現自己的無鎖資料結構了,下面我們就嘗試著來實現一個無鎖棧:

下面的程式碼中,我們假設malloc與free是執行緒安全的(至於malloc與free的可重入性與執行緒安全問題,可以看這裡這裡)

首先定義必要的資料結構:
  1. /*定義CAS操作*/
  2. #define CAS __sync_bool_compare_and_swap
  3. /*
  4. * 定義stack的資料結構
  5. */
  6. typedef struct stack_node {
  7.     struct node *next; /*指向下一個節點,即通過連結串列的形式實現棧*/
  8.     void *data;/*指向該節點的資料*/
  9. }
    stack_node;
  10. /*棧頂指標*/
  11. stack_node *top = NULL;
接下來是棧操作:
  1. /*壓棧操作*/
  2. void stack_push(void *data)
  3. {
  4.     stack_node *new = malloc(sizeof(struct node));
  5.     new->data = data;
  6.     do {
  7.         new->next = top;/*獲取top的快照, 同時初始化了new的next指標*/
  8.     }while(!CAS(&top, new->next, new));
  9. }
在取得top的快照後後, 就通過CAS操作檢視top的內容與line 7取得的快照是否一致,如果一致則將top的內容更換為new,CAS函式返回true。
假設有執行緒A在stack_push裡獲取top快照後暫時失去了執行權, 切換至另一個執行緒B, 而執行緒B完成了一次stack_push操作,然後執行權再次回到執行緒A,
執行緒A執行CAS操作, 發現top裡的內容與快照裡的內容已經不一致了,CAS返回false, 估do...while語句重新執行。

接下來是出棧操作:
  1. /*出棧操作*/
  2. void * stack_pop(void)
  3. {
  4.     stack_node *tmp;
  5.     void *data;
  6.     do {
  7.         tmp = top;
  8.         if (!tmp) return NULL;
  9.     }while(CAS(&top, tmp, tmp->next) = true);
  10.     data = tmp->data;
  11.     free(tmp);
  12.     return data;
  13. }
其免鎖原理與入棧基本一樣,估不再贅述。

另外還有一點, 在使用CAS實現免鎖資料結構時, 容易出現ABA問題, 這裡也暫時不作討論, 可以從參考資料中得到更多的資訊。

參考資料:
1.來自酷客的無鎖佇列
2.設計不用互斥鎖的併發資料結構

轉載自:http://blog.chinaunix.net/uid-25424552-id-3772253.html

相關推薦

執行模型程式設計

多執行緒模式是比較流行的一種併發程式設計模型,多執行緒程式設計的一個特點就是執行緒間共享記憶體空間;這可以降低執行緒間通訊的開銷,但卻引來了另外的一個難纏的問題:竟態條件!,因此,甚至有人對多執行緒模型提出了質疑,看這裡。在多執行緒程式設計模型下,解決竟態條件的傳統方法就是

c++執行模式的socket程式設計執行池實現)

     socket 程式設計可以說是一個基本的技術掌握,而多個客戶端向服務端傳送請求又是一個非常常見的場景,因此多執行緒模式下的socket程式設計則顯得尤為常見與重要。     本文主要利用執行緒池的技術,來實現多執行緒的模式,執行緒池的優點就不多述了,相信大家都能理

一種執行基於計數實現(C#)(轉載)

轉自:http://blog.csdn.net/chzuping/article/details/10960061   chzuping的專欄 本文介紹一種不加鎖,不使用原子操作的多執行緒同步機制。先申明下,該方案為我在實際程式設計中創造出來的,事先我沒有在其中地方

socket程式設計執行模型

/*問題 1.調整程序內的最大檔案描述符上限 2.執行緒如有共享資料,考慮執行緒同步 3.服務與客戶端執行緒退出時,退出處理 4.系統負載,隨著連結客戶端增加,導致其他執行緒不能及時得到CPU */ /*server.c*/ #include <stdio.h> #includ

執行 共享資源 同步 java Java執行程式設計:Lock

Java多執行緒程式設計:Lock   synchronized是java中的一個關鍵字,也就是說是Java語言內建的特性。那麼為什麼會出現Lock呢?   如果一個程式碼塊被synchronized修飾了,當一個執行緒獲取了對應的鎖,並執行該程式碼塊時,其他執行緒便只

localtime_r在執行環境可能存在死

         但是在某些情況下,localtime_r可能存在死鎖的情況,使用如下的測試程式:        #include <pthread.h> #include <time.h>        void *mytest(void *arg) {         pthrea

網路程式設計執行——GIL全域性直譯器

網路程式設計之多執行緒——GIL全域性直譯器鎖 一、引子 定義: In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python b

OSG 執行模型 設計思想

A New Processing Model for Multithreaded, Multidisplay Scene Graphs Copyright © 2001 Don Burns (DB - Apr 28, 2004) This article

執行基礎5 Lock

1.同步 * 使用ReentrantLock類的lock()和unlock()方法進行同步 2.通訊 * 使用ReentrantLock類的newCondition()方法可以獲取Condition物件 * 需要等待的時候使用Condition的await()方法, 喚醒的時候用signal()

java執行中顯式的輪詢檢測策略

顯式鎖簡介 java5.0之前,在協調對共享物件的訪問時可以使用的機制只有synchronized和volatile,java5.0增加了一種新的機制:ReentrantLock。 鎖像synchronized同步塊一樣,是一種執行緒同步機制,與synchronized不同的是ReentrantLock提

【JAVA執行問題之死

  一、死鎖是什麼? 舉個例子:兩個人一起吃飯,每個人都拿了一隻筷子,雙方都在等待對方將筷子讓給自己,結果兩個人都吃不了飯。這種情況和計算機中的死鎖情況很相似。 假設有兩個執行緒,互相等待對方釋放佔有的鎖,但是釋放鎖的條件又不可能形成,這時候死鎖就形成了。 還是買票的問題,有的時候時會發生死

執行學習----讀寫的使用(十二)

讀寫鎖      讀寫鎖:分為讀鎖和寫鎖,多個讀鎖不互斥,讀鎖與寫鎖互斥,寫鎖和寫鎖互斥,這是由jvm自己控制的,你只要上好相應的鎖即可。如果你的程式碼只讀資料,可以很多人同時讀,但是不能同時寫,那就讓讀鎖;如果你的程式碼修改資料,只能有一個人在寫,且不能同時讀

gdb除錯執行出現的死

   多執行緒的條件下,程式很容易出現死鎖,此時各個執行緒處於等待狀態,可以通過gdb除錯找到死鎖出現的地方。 例子: #include <stdio.h> #include <pthread.h> #include <uni

執行-day-10顯示

目錄 顯示鎖 Lock介面和核心方法 Lock和synchronized關鍵字的比較 可重入鎖ReentrantLock、公平鎖、非公平鎖 讀寫鎖 Condition介面 用Lock和Condition實現等待和通知 一、Lock介面和核心方法  

java執行:5.1 -基礎

什麼是鎖 提到多執行緒,立馬就有人說加鎖,什麼是鎖,為什麼加鎖? 鎖:從字面意義,什麼東西加了鎖,那麼就只有有鑰匙的人才能使用,多執行緒中的鎖,也是這個意思。 為什麼加鎖:當單執行緒的時候,無論訪問什麼資源,都不需要考慮鎖的問題,但是當多個執行緒訪問同一個資源,就會發生很多千奇百怪的

HashMap執行環境的死迴圈問題解釋

hashMap在多執行緒環境下,呼叫put方法出現的死迴圈是由於擴容時候resize方法導致的連結串列出現迴圈。 void resize(int newCapacity) { Entry[] oldTable = table; int oldCapacity =

基於Log4j NDC 執行條件記錄日誌,排查生產問題

在大吞吐,高併發的場景, 一個請求到達後端,通常是轉換成多執行緒並行處理請求, 如何通過一個標誌找到這個請求對應的多個執行緒,瞭解這個請求的完整的處理情況,從而幫助我們定位這次呼叫到底哪一步出現了問題,對我們快速定位排查生產問題是非常有用的。我們在每個子任務程式

java day25 執行) 單例類(Runtime,Timer

25.01_多執行緒(單例設計模式)(掌握) 單例設計模式:保證類在記憶體中只有一個物件。 如何保證類在記憶體中只有一個物件呢? (1)控制類的建立,不讓其他類來建立本類的物件。private (2)在本類中定義一個本類的物件。Singl

java執行--簡易使用同步實現一對一交替列印

一、本例需要分析的地方不多,只需要使用一個同步鎖+一個計數器就能搞定,直接奉送原始碼吧: package com.example.liuxiaobing.statemodel.mutil_thr

為什麼EventLoop能避免執行併發操作和競爭

Netty的Reactor執行緒池就是EventLoopGroup,是一個EventLoop的陣列。EventLoop是用來處理所有註冊到自身這個執行緒的Selector上的channel,Selector的輪詢操作由EventLoop的run方法驅動,在一個迴圈體內迴圈執行,包括使用者自定