多執行緒併發產生的原因

2022-06-17 09:24:15 字數 1664 閱讀 3864

背景

先看下面一段**,看看執行結果

class

program

}public

class

accounttest}}

有乙個accounttest類,類裡面有乙個account值,有乙個add方法功能是把account值累加100萬次;

main方法裡面開啟了兩個任務,兩個任務共用乙個accounttest例項,兩個任務的功能都是使account值累加100萬次;

正常來講當兩個任務都執行完畢後account輸出的值應該為200萬;

但是最後輸出的account值為 100萬到200萬之間的隨機數。

原因

由於cpu和內存在處理速度上存在很大差距,為了彌補這種差距,也是為了利用好cpu強大的計算能力,cpu和記憶體之間加入了快取,也就是我們經常聽到的暫存器快取、l1、l2和l3快取;

首先我們看一下程式一般的處理流程如下圖所示:

在多核時代,有多個cpu,意味著每個cpu都有自己的一套快取體系,但是記憶體只有乙份;每個cpu裡快取的共享資料可能不一樣,如果我們拿著這些本地快取的資料做業務計算,極有可能出問題;

如上圖所示:執行緒1執行在cpu-01,執行緒2執行在cpu-02,著兩個執行緒都並行執行,一開始執行緒1和執行緒2都從記憶體中取出變數m值都為0,然後都把變數m快取起來,後來執行緒2修改了變數m的值為2同時寫回了記憶體,這時記憶體中變數m的值為2,但是此時執行緒1快取的變數m值還是為1不是最新的2,執行緒1還是使用變數m為1做業務計算,如果兩個執行緒都是使m加1,這時執行緒1會覆蓋執行緒2的修改,導致記憶體中m值只加了1次1,而不是兩次1。

解決方法

c#的volatile關鍵字可以實現可見性,即告訴cpu程式不想使用你的快取,所有的執行緒都讀寫記憶體資料,同時該關鍵字還能禁止cpu指令重排和優化;

但是如果我們只在accounttest裡面加上volatile關鍵字還是有問題

public volatile int account = 0;

因為在多核環境下可能存在多個執行緒同時讀取記憶體裡的共享資料,資料處理好後,然後同時更新寫入的情況,這樣還是有覆蓋更新的問題;

解決方法1:對共享資源加鎖,使得同一時刻只能乙個執行緒使用共享資源

使用lock改造後的accounttest如下:

public

class

accounttest}}

}

解決方法2 :使用原子操作

首先我們找出執行緒不安全的部分就是add方法裡的 ++this.account;

這分為三步:

1 先從記憶體中讀取account值

2 將account+1

3 把account+1值寫回快取

如果這三步是原子操作的話,那麼這個類就是執行緒安全的

使用c#的interlocked原子操作改寫accounttest如下

public

class

accounttest}}

(多執行緒)多執行緒的併發安全

多執行緒併發操作同乙個資源 同步鎖 多執行緒操作的鎖必須唯一 必須搞清楚 哪些 需要同步?那些在操作共享資源的 只要包含非讀的操作,或者根據共享資源進行條件判斷的,就需要同步!同步 塊解決 package com.gc.thread 多執行緒操作共享資源 併發 執行緒安全問題 同步 鎖 相對而言效能...

多執行緒併發

多執行緒併發主要有3個方面 1 同步器 主要有synchronized,reentrantlock 訊號量,門栓 countdownlatch 障柵 cyclicbarrier 交換器。2 同步容器 主要包括 對映 集 佇列 對映 concurrenthashmap,concurrentskipli...

多執行緒併發

更簡單的執行緒池 多執行緒和多程序都可以很容易的實現併發,協程通過切換上下文來充分利用cpu實現併發效果 threading模組 thread類的基本狀態和行為 屬性名和值 name none,group none,target none,args kwargs daemon none 方法 sta...