是否足以實現一次只有一個點可以檢查地點是否已經被發現的情況?
是。但是有什麼意義呢?
如果線程正在等待其他線程花費所有時間,那麼您從多線程中獲得了什麼?
有三個(有時是重疊的)原因,產生更多的線程:
- 要使用多個核心的同時:總吞吐量增加。
- 當另一個線程正在等待其他事情(通常是來自文件,數據庫或網絡的I/O)時完成工作:整體吞吐量增加。
- 在工作完成時對用戶交互作出響應:總體吞吐量下降,但用戶感覺速度更快,因爲他們分別正在作出反應。
這裏最後不適用。
如果您的「檢查」涉及I/O,那麼第二個可能適用,並且此策略可能有意義。
第一個可以很好地應用,但是因爲所有線程都花費大部分時間在其他線程上等待,所以吞吐量並沒有得到改善。
事實上,因爲在設置線程和切換它們之間存在開銷,所以這個代碼將比只有一個線程處理所有事情要慢:如果一次只能有一個線程工作,那麼只有一個線程!
所以你在這裏使用鎖是正確的,因爲它可以防止腐敗和錯誤,但毫無意義,因爲它使一切都變得太慢。
怎麼辦這個問題:
如果你的真實案例涉及I/O或其他原因線程其實花費大部分時間都是在彼此照顧對方的方式,那麼你有什麼是好的。
否則你有兩個選擇。
簡單:只需使用一個線程。硬:有更好的鎖定。有精細的鎖定
一種方式是做雙重檢查:
static void check_place(int x, int y)
{
if (!discovered[x, y])
lock (ob)
if (!discovered[x, y])
{
discovered[x, y] = true;
num_discovered += 1;
}
}
現在至少是一些線程會跳過某些情況下discovered[x, y]
是true
無阻礙了其他線程。
當線程將在鎖定期結束時獲得結果時,這非常有用。儘管如此,它仍然不夠好,因爲它只是快速轉向一個案件,它再次爲鎖定而戰。
如果我們的discovered
查找是本身線程安全的,線程安全是細粒度的,那麼我們就可以取得一些進展:
static void check_place(int x, int y)
{
if (discovered.SetIfFalse(x, y))
Interlocked.Increment(ref num_discovered)
}
到目前爲止,雖然我們剛剛搬來搬去的問題;我們如何使SetIfFalse
線程安全而不使用單個鎖並導致相同的問題?
有幾種方法。我們可以使用條帶鎖或低鎖定併發集合。
這似乎是你有一個50×50大小固定的結構,在這種情況下,這是不是太狠:
private class DotMap
{
//ints because we can't use interlocked with bools
private int[][] _map = new int[50][];
public DotMap()
{
for(var i = 0; i != 50; ++i)
_map[i] = new int[50];
}
public bool SetIfFalse(int x, int y)
{
return Interlocked.CompareExchange(ref _map[x][y], 1, 0) == 0;
}
}
現在,我們的優勢是:
- 我們所有的鎖定要低得多(但請注意,在競爭面前,
Interlocked
的操作仍然會減慢,儘管不如lock
)。
- 我們的大部分鎖定都不受其他鎖定的限制。具體而言,在
SetIfFalse
中可以允許檢查單獨的區域,而不用彼此分開。
這既不是萬能的,但(這種方法仍然遭受爭的臉,也帶來了自己的成本),也易於推廣到其他情況下(改變SetIfFalse
的東西,做任何事情超過檢查和更改單一的價值並不容易)。即使在擁有大量內核的機器上,它仍然很可能比單線程方法慢。
另一種可能性是沒有SetIfFalse
線程安全可言,但要保證每個相互分隔的線程,讓他們永遠也不會打相同的價值觀和,該結構是安全的這種多線程訪問(在線程只遇到不同索引時必須是線程安全的,而且必須是可變結構,其中Add
和/或Remove
不是可變結構)的情況。
總而言之,你有正確的想法,關於如何使用lock
來防止線程導致錯誤,這就是98%的時間使用多線程的方法,因爲它涉及線程等待在別的東西上。你的例子雖然鎖定太多以致於無法從多核心獲益,並且創建代碼並不重要。
是你的問題'發現[x,y]'和'num_discovered'訪問是否是線程安全的?我們需要爲此看到「ob」的聲明。 – CodeCaster
爲什麼每個點都有一個線程?我可以想象,如果點數少,每個點需要大量處理能力,這對您的應用程序實時運行至關重要。如果它是一個學習線程的練習項目,那也很好。否則,這聽起來像是對我來說可維護性。 – MariusUt
當線程調用check_place時線程之間不會存在爭用嗎?爲什麼你需要爲每個點有一個線程? – auburg