多線程無並發用到共享資源,還需要加鎖嗎?

自己機子運行一個程序,主線程要調用一個方法,這個方法比較耗時(0.1s),會使主線程卡一下,這個方法被調用頻率最高為0.3s左右,平時也不會調用。

這個方法會讀取並寫入一個共享數據。我用多線程調用這個方法,使得主線程不會卡頓。

那麼問題就來了,雖然用多線程也調用到了共享數據,但是根本不會並發(新線程執行的時候,前一個線程肯定都執行完了啊),這種情況下,還要對共享資源加鎖嗎?


如果你能證明同時只有一個線程在訪問,那當然不用加鎖了,只是這樣比較危險,因為這種事情很難算對。


看寫法了。多線程讀寫不加鎖的話,即便在時間上實際是按照順序在執行的,也會有理論上的可能因為Memory Model而產生問題(當然你這個頻率這麼低……還比如像C# 2.0開始all writes are volatile就沒事了)。總之,看情況,最好交給現成的類庫來做,例如丟到個隊列裡面去云云。


加啊。過早的優化,是萬惡之源……


選擇好線程模型,就不需要自己加鎖。下面解釋下如何選擇線程模型。

首先看問題,從描述看,主要問題是有個函數比較慢,會阻塞主線程。如果沒有其它需求。那麼使用其它線程來負責這個函數調用就可以了。主線程只需要創建任務然後派發到其它線程執行。

根據問題的環境設定與上述方案,可以按「其它線程」的數量分兩種情況討論。

「其它線程」數大於1:雖然主線程的調用頻率不足以發生重疊,但是多線程下還是可能會發生重疊。因為操作系統不能保證各個線程的時間片的分配是均勻的。在其它線程數大於1,而且不加鎖的情況下,那麼就可能會出現這種執行結果:(M是主線程;1,2表示其它兩個線程。)

M:---&>A--------&>B---------------------

1 :-----AAAAAAAAAAAAAAAAAA------

2 :-----------------BBBBBBB--------

A,B表示主線程對這個函數的兩次調用,可能存在這種情況:A晚於B結束,於是對共享資源訪問衝突的可能性一定會有。你說測得函數執行時間是0.1秒,這個只具有統計意義,常見的高級的多線程操作系統(包括Linux,Windows)都無法保證每次調用都在x秒內完成。所以,題目中的描述「新線程執行的時候,前一個線程肯定都執行完了啊」是個偽命題。於是就必須要在訪問共享資源時加鎖。

「其它線程」數為1:可以保證函數的執行順序。就沒有必要自己加鎖。這裡強調不用自己加鎖,並不表示你調用的類庫里也一定沒有鎖。從一個線程派發任務到另一個線程這個操作對你來說可能就是一個函數調用(Java里的ExecutorService或是.NET的TPL,ThreadPool,甚至Timer,隨便。),你不用自己加鎖,但是承載線程間消息的數據體對這兩個線程而言就是共享資源,並被兩個線程訪問,但類庫已經保證這些函數是線程安全的。但是至於它們到底有沒有用鎖,這得去看類庫的源代碼。它倒是完全可以使用一個Lock-Free的數據結構,我沒看過,我不確定。

有幾個線程,是可以也應該是自己控制的;但是這幾個線程是如何被系統執行,是無法控制的,也應該時間謹記是無法控制的(比如不要假設一個函數的執行時間)。


問題的根源不是多線程與否 而是是否線性訪問。

線性訪問資源就是無競爭

無競爭就是非並發

當然不用加鎖。


要控制,這個0.1s只是現在,方法可能會被擴充,在不同機器性能下結果也不樣,別給自己埋坑啊


如果你這個版本還有繼續擴展的可能,那一定要加。不加的話後期需求變更的會變得更加困難。

很多架構無法優化就是因為底層被寫死,牽一髮動全身。


不加總會撞上出問題的那一刻,曾經被多線程的靈異問題搞到崩潰,最後都是要麼加鎖要麼內存上做隔離保證多線程不會操作同一內存。


按照描述來推測,加鎖的時間開銷相對於執行來說是很小很小的,頻率也不高。加鎖沒錯。

線性訪問確實不用加鎖了。但這畢竟是在多線程環境下,沒準哪天隨著功能升級、調整,說不定就不是線性訪問了呢……


既然你的程序假設同時只會有一個線程來處理,那幹嘛不寫代碼來保證這一點?

不知道你的代碼上下文是什麼樣子,只是給你一些可能相關的idea,你可以考慮用:

1. 如果是周期性的工作,用 System.Timers.Timer,在worker thread處理之前把這個timer給stop掉,處理完了再enable

2. 如果是用戶輸入來執行任務,可以用一個生產者/消費者隊列,capacity為1,具體可以用BlockingCollection

Update: 我暈了,還以為題主是用C#


加鎖肯定沒錯,不加鎖誰知道什麼時候出什麼亂七八糟的問題。這種事情就是寧殺錯不放過。


傳統項目中什麼場合需要多線程?


推薦閱讀:

單線程中加鎖代碼的性能如何?
redis為什麼是單線程?在多核處理器下對主存的訪問真的比多線程更有效率?未來有可能改用多線程嗎?
malloc和free是線程安全的嗎,在多線程開發時用這兩個函數應該注意什麼?
Python有GIL為什麼還需要線程同步?
像網路爬蟲、資料庫等方面多線程程序如何設置線程數?

TAG:多線程 | 鎖具 | 並發 |