簡體   English   中英

如何使靜態變量線程安全

[英]How to make a static variable thread-safe

我有這個靜態類,它包含一個靜態變量(一個簡單的int)。 我已經在線程的Run()方法中實現了一個lock() ,因此沒有其他線程可以同時訪問這個類,但變量仍然會變得瘋狂,顯示重復,極其高的值等。

這是班級:

public static class ExplorationManager
{
    public static int Counter = 0;

    public static void ExplorerMaker(List<int[]> validPaths, List<string> myParents, string[,] myExplorationMap, List<int[]> myPositions)
    {
        foreach (var thread in validPaths.Select
        (path => new Explorer(myParents, path, myExplorationMap, myPositions)).
        Select(explorer => new Thread(explorer.Explore)))
            {
                thread.Name = "Thread of " + Counter + " generation";
                Counter++; 
                thread.Start();
    }
}

}

有沒有辦法讓這個變量“更”線程安全?

為了提高此類安全性,您需要解決至少2個問題。

第一個是讓Counter private 在它的當前形式中,變量是100%公開的,並且它可以被應用程序中的任何代碼片段變異。 今天它可能是安全的,但沒有什么能保護你明天犯錯。 如果您仍希望其他代碼段能夠讀取屬性,則使用訪問器

private static int m_counter;
public static int Counter {
  get { return m_counter; }
}

第二個問題是++不是在線程之間共享的位置上的安全操作。 它擴展為以下代碼

Counter = Counter + 1;

這實際上在做什么

  1. 加載計數器
  2. 載入1
  3. 商店櫃台

幾乎任何時候都可以中斷線程。 如果一個線程在步驟1,2或3中斷,另一個線程完全執行序列,那么您將最終添加/存儲過時值。 這就是++不安全的原因。 在線程之間增加共享值的安全方法是使用Interlocked.Increment 它的設計正是為了這個目的

Interlocked.Increment(ref m_counter);

使用Interlocked類:

Interlocked.Increment(ref Counter);

您需要在靜態變量的所有讀/寫周圍使用lock 就像是:

public static readonly object CounterLock = new object();

...
lock ( CounterLock )
{
    Counter++;
}
...

關鍵是所有讀/寫必須受到鎖的保護 - 它不足以保護單個位置,因為當其他地方的鎖生效時,執行讀或寫的線程仍然可能會發生變化。

鎖保護代碼區域而不是變量,這就是為什么在訪問共享變量時需要鎖定的原因。

請注意,您無法鎖定Counter變量 - 您需要引用類型的實例作為鎖,而不是值類型。 這就是我使用object作為鎖類型的原因(另一個答案也是如此)。

像這樣的東西應該做的伎倆:

public static class ExplorationManager
{
    public static int Counter = 0;
    private static object _lock = new object();

    public static void ExplorerMaker(List<int[]> validPaths, List<string> myParents, string[,] myExplorationMap, List<int[]> myPositions)
    {
        foreach (var thread in validPaths.Select
        (path => new Explorer(myParents, path, myExplorationMap, myPositions)).
        Select(explorer => new Thread(explorer.Explore)))
            {
                thread.Name = "Thread of " + Counter + " generation";
                lock(_lock)
                {
                    Counter++; 
                    thread.Start();
                }
    }
}

您可以嘗試使用靜態構造函數來初始化靜態變量。 最佳做法是提供單獨的locking對象,以便您可以很好地控制鎖的粒度。

Interlocked.Increment是另一個線程安全選項。 如果你只是需要反擊,那么使用起來很簡單。

var newCounter = Interlocked.Increment(ref Counter)
thread.Name = "Thread of " + (newCounter-1) + " generation";

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM