C# 如何使静态变量线程安全

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12981190/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 01:12:21  来源:igfitidea点击:

How to make a static variable thread-safe

c#multithreading

提问by Jean Carlos Suárez Marranzini

I have this static class which contains a static variable (a simple int). I've implemented a lock()in the Run()method of the threads, so no other threads can access to this class concurrently, but the variable still goes crazy, displaying duplicates, insanely high values, etc.

我有这个静态类,它包含一个静态变量(一个简单的 int)。我已经在线程lock()Run()方法中实现了 a ,所以没有其他线程可以同时访问这个类,但是变量仍然很疯狂,显示重复,疯狂的高值等。

This is the class:

这是课程:

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();
    }
}

}

}

Is there a way to make this variable "more" thread-safe?

有没有办法让这个变量“更”线程安全?

采纳答案by JaredPar

There are at least 2 problems that you need to address in order to increase the safety of this type.

为了提高这种类型的安全性,您至少需要解决两个问题。

The first one is to make Counterprivate. In it's current form the variable is 100% public and it can be mutated by any piece of code in the application. Today it may be safe but there's nothing protecting you from making a mistake tomorrow. If you still want other pieces of code to be able to read the property then use an accessor

第一个是使Counterprivate. 在它的当前形式中,该变量是 100% 公开的,并且它可以被应用程序中的任何代码段改变。今天可能是安全的,但没有什么可以保护你明天不犯错误。如果您仍然希望其他代码段能够读取该属性,请使用访问器

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

The second problem is that ++isn't a safe operation on a location that is shared amongst threads. It expands out to the following code

第二个问题是++在线程之间共享的位置上的操作不是安全的。它扩展为以下代码

Counter = Counter + 1;

Which is in reality doing

这实际上是在做什么

  1. load Counter
  2. load 1
  3. add
  4. store Counter
  1. 负载计数器
  2. 负载 1
  3. 添加
  4. 专卖店柜台

A thread can be interrupted an virtually any time. If one thread is interrupted at step 1, 2 or 3 and another thread fully executes the sequence then you will end up adding / storing stale values. This is why ++is unsafe. The safe way to increment a shared value amongst threads is to use Interlocked.Increment. It's designed exactly for this purpose

线程几乎可以随时中断。如果一个线程在第 1、2 或 3 步中断,而另一个线程完全执行该序列,那么您最终将添加/存储陈旧值。这++就是不安全的原因。在线程之间增加共享值的安全方法是使用Interlocked.Increment. 它正是为此目的而设计的

Interlocked.Increment(ref m_counter);

回答by xxbbcc

You need to use lockaround all reads/writes of your static variable. Something like:

您需要使用lock静态变量的所有读/写。就像是:

public static readonly object CounterLock = new object();

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

The point is that allreads / writes must be protectedby the lock - it's not enough to protect a single place because then threads doing reads or writes may still make a change when a lock elsewhere is in effect.

关键是所有的读/写都必须受到锁的保护——保护一个地方是不够的,因为当其他地方的锁生效时,执行读或写的线程可能仍然会做出改变。

A lock protects a region of code, not a variable, that's why you need a lock everywhere where you access a shared variable.

锁保护的是代码区域,而不是变量,这就是为什么在访问共享变量的任何地方都需要锁的原因。

Note that you cannot lock on your Countervariable - you need an instance of a reference type as a lock, not a value type. This is why I used objectas the lock type (the other answer did the same).

请注意,您不能锁定您的Counter变量 - 您需要一个引用类型的实例作为锁,而不是值类型。这就是我用作object锁类型的原因(另一个答案也是如此)。

回答by Rahul Tripathi

You can try with static constructor to initialize static variables. It's best practice to provide a separate lockingobject, so you have good control over granularity of locks.

您可以尝试使用静态构造函数来初始化静态变量。最好的做法是提供一个单独的locking对象,这样你就可以很好地控制锁的粒度。

回答by Jordan Kaye

Something like this should do the trick:

像这样的事情应该可以解决问题:

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();
                }
    }
}

回答by Eren Ers?nmez

Use the Interlockedclass:

使用互锁类:

Interlocked.Increment(ref Counter);

回答by Alexei Levenkov

Interlocked.Incrementis another thread-safe option. Much simple to use if you just need counter.

Interlocked.Increment是另一个线程安全选项。如果您只需要计数器,则使用起来非常简单。

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