C# 静态构造函数线程安全吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7095/
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
Is the C# static constructor thread safe?
提问by urini
In other words, is this Singleton implementation thread safe:
换句话说,这个 Singleton 实现线程是否安全:
public class Singleton
{
private static Singleton instance;
private Singleton() { }
static Singleton()
{
instance = new Singleton();
}
public static Singleton Instance
{
get { return instance; }
}
}
采纳答案by Zooba
Static constructors are guaranteed to be run only once per application domain, before any instances of a class are created or any static members are accessed. https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/static-constructors
在创建类的任何实例或访问任何静态成员之前,静态构造函数保证每个应用程序域仅运行一次。https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/static-constructors
The implementation shown is thread safe for the initial construction, that is, no locking or null testing is required for constructing the Singleton object. However, this does not mean that any use of the instance will be synchronised. There are a variety of ways that this can be done; I've shown one below.
所示的实现对于初始构造是线程安全的,也就是说,构造 Singleton 对象不需要锁定或空测试。然而,这并不意味着该实例的任何使用都会被同步。有多种方法可以做到这一点;我在下面展示了一个。
public class Singleton
{
private static Singleton instance;
// Added a static mutex for synchronising use of instance.
private static System.Threading.Mutex mutex;
private Singleton() { }
static Singleton()
{
instance = new Singleton();
mutex = new System.Threading.Mutex();
}
public static Singleton Acquire()
{
mutex.WaitOne();
return instance;
}
// Each call to Acquire() requires a call to Release()
public static void Release()
{
mutex.ReleaseMutex();
}
}
回答by Dominic Cooney
The Common Language Infrastructure specificationguarantees that "a type initializer shall run exactly once for any given type, unless explicitly called by user code." (Section 9.5.3.1.) So unless you have some whacky IL on the loose calling Singleton::.cctor directly (unlikely) your static constructor will run exactly once before the Singleton type is used, only one instance of Singleton will be created, and your Instance property is thread-safe.
在公共语言基础规范保证“一类型初始不得以任何给定类型刚好运行一次,除非用户代码显式调用。” (第 9.5.3.1 节。)因此,除非您有一些奇怪的 IL 松散地直接(不太可能)调用 Singleton::.cctor,否则您的静态构造函数将在使用 Singleton 类型之前只运行一次,只会创建一个 Singleton 实例,并且您的 Instance 属性是线程安全的。
Note that if Singleton's constructor accesses the Instance property (even indirectly) then the Instance property will be null. The best you can do is detect when this happens and throw an exception, by checking that instance is non-null in the property accessor. After your static constructor completes the Instance property will be non-null.
请注意,如果 Singleton 的构造函数访问 Instance 属性(甚至是间接访问),则 Instance 属性将为空。您能做的最好的事情是检测何时发生并抛出异常,方法是在属性访问器中检查该实例是否为非空。静态构造函数完成后,Instance 属性将为非空。
As Zoomba's answerpoints out you will need to make Singleton safe to access from multiple threads, or implement a locking mechanism around using the singleton instance.
正如Zoomba 的回答指出的那样,您需要使 Singleton 安全地从多个线程访问,或者围绕使用单例实例实现锁定机制。
回答by Derek Park
Using a static constructor actually isthreadsafe. The static constructor is guaranteed to be executed only once.
使用静态构造函数实际上是线程安全的。静态构造函数保证只执行一次。
From the C# language specification:
The static constructor for a class executes at most once in a given application domain. The execution of a static constructor is triggered by the first of the following events to occur within an application domain:
- An instance of the class is created.
- Any of the static members of the class are referenced.
类的静态构造函数在给定的应用程序域中最多执行一次。静态构造函数的执行由应用程序域中发生的以下第一个事件触发:
- 类的一个实例被创建。
- 引用类的任何静态成员。
So yes, you can trust that your singleton will be correctly instantiated.
所以是的,您可以相信您的单身人士会被正确实例化。
Zooba made an excellent point (and 15 seconds before me, too!) that the static constructor will not guarantee thread-safe shared access to the singleton. That will need to be handled in another manner.
Zooba 提出了一个很好的观点(也是比我早 15 秒!)静态构造函数不能保证对单例的线程安全共享访问。这将需要以另一种方式处理。
回答by Andrew Peters
Static constructors are guaranteed to fire only once per App Domain so your approach should be OK. However, it is functionally no different from the more concise, inline version:
静态构造函数保证每个应用程序域仅触发一次,因此您的方法应该没问题。但是,它在功能上与更简洁的内联版本没有区别:
private static readonly Singleton instance = new Singleton();
Thread safety is more of an issue when you are lazily initializing things.
当您懒惰地初始化事物时,线程安全更像是一个问题。
回答by Eran Kampf
Static constructor is guaranteed to be thread safe. Also, check out the discussion on Singleton at DeveloperZen: http://www.developerzen.com/2007/07/15/whats-wrong-with-this-code-1-discussion/
静态构造函数保证线程安全。另外,请查看 DeveloperZen 上关于 Singleton 的讨论:http: //www.developerzen.com/2007/07/15/whats-wrong-with-this-code-1-discussion/
回答by Brian Rudolph
While all of these answers are giving the same general answer, there is one caveat.
虽然所有这些答案都给出了相同的一般答案,但有一个警告。
Remember that all potential derivations of a generic class are compiled as individual types. So use caution when implementing static constructors for generic types.
请记住,泛型类的所有潜在派生都被编译为单独的类型。因此,在为泛型类型实现静态构造函数时要小心。
class MyObject<T>
{
static MyObject()
{
//this code will get executed for each T.
}
}
EDIT:
编辑:
Here is the demonstration:
这是演示:
static void Main(string[] args)
{
var obj = new Foo<object>();
var obj2 = new Foo<string>();
}
public class Foo<T>
{
static Foo()
{
System.Diagnostics.Debug.WriteLine(String.Format("Hit {0}", typeof(T).ToString()));
}
}
In the console:
在控制台中:
Hit System.Object
Hit System.String
回答by Florian Doyon
Just to be pedantic, but there is no such thing as a static constructor, but rather static type initializers, here's a smalldemo of cyclic static constructor dependency which illustrates this point.
只是为了迂腐,但没有静态构造函数之类的东西,而是静态类型初始化器,这里有一个循环静态构造函数依赖的小演示,它说明了这一点。
回答by Jay Juch
Here's the Cliffnotes version from the above MSDN page on c# singleton:
这是上面 C# 单例 MSDN 页面中的 Cliffnotes 版本:
Use the following pattern, always, you can't go wrong:
始终使用以下模式,您不会出错:
public sealed class Singleton
{
private static readonly Singleton instance = new Singleton();
private Singleton(){}
public static Singleton Instance
{
get
{
return instance;
}
}
}
Beyond the obvious singleton features, it gives you these two things for free (in respect to singleton in c++):
除了明显的单例特性之外,它还免费为您提供了以下两项功能(相对于 C++ 中的单例):
- lazy construction (or no construction if it was never called)
- synchronization
- 懒惰构造(如果从未调用过,则不构造)
- 同步
回答by oleksii
Although other answers are mostly correct, there is yet another caveat with static constructors.
尽管其他答案大多是正确的,但静态构造函数还有另一个警告。
As per section II.10.5.3.3 Races and deadlocksof the ECMA-335 Common Language Infrastructure
由于每节II.10.5.3.3竞争和死锁的的ECMA-335通用语言基础
Type initialization alone shall not create a deadlock unless some code called from a type initializer (directly or indirectly) explicitly invokes blocking operations.
除非从类型初始值设定项(直接或间接)调用的某些代码显式调用阻塞操作,否则单独的类型初始化不应创建死锁。
The following code results in a deadlock
以下代码导致死锁
using System.Threading;
class MyClass
{
static void Main() { /* Won't run... the static constructor deadlocks */ }
static MyClass()
{
Thread thread = new Thread(arg => { });
thread.Start();
thread.Join();
}
}
Original author is Igor Ostrovsky, see his post here.
原作者是 Igor Ostrovsky,请在此处查看他的帖子。
回答by Trade-Ideas Philip
The static constructor will finishrunning beforeany thread is allowed to access the class.
静态构造函数将在允许任何线程访问该类之前完成运行。
private class InitializerTest
{
static private int _x;
static public string Status()
{
return "_x = " + _x;
}
static InitializerTest()
{
System.Diagnostics.Debug.WriteLine("InitializerTest() starting.");
_x = 1;
Thread.Sleep(3000);
_x = 2;
System.Diagnostics.Debug.WriteLine("InitializerTest() finished.");
}
}
private void ClassInitializerInThread()
{
System.Diagnostics.Debug.WriteLine(Thread.CurrentThread.GetHashCode() + ": ClassInitializerInThread() starting.");
string status = InitializerTest.Status();
System.Diagnostics.Debug.WriteLine(Thread.CurrentThread.GetHashCode() + ": ClassInitializerInThread() status = " + status);
}
private void classInitializerButton_Click(object sender, EventArgs e)
{
new Thread(ClassInitializerInThread).Start();
new Thread(ClassInitializerInThread).Start();
new Thread(ClassInitializerInThread).Start();
}
The code above produced the results below.
上面的代码产生了下面的结果。
10: ClassInitializerInThread() starting.
11: ClassInitializerInThread() starting.
12: ClassInitializerInThread() starting.
InitializerTest() starting.
InitializerTest() finished.
11: ClassInitializerInThread() status = _x = 2
The thread 0x2650 has exited with code 0 (0x0).
10: ClassInitializerInThread() status = _x = 2
The thread 0x1f50 has exited with code 0 (0x0).
12: ClassInitializerInThread() status = _x = 2
The thread 0x73c has exited with code 0 (0x0).
Even though the static constructor took a long time to run, the other threads stopped and waited. All threads read the value of _x set at the bottom of the static constructor.
即使静态构造函数需要很长时间才能运行,其他线程也会停止并等待。所有线程都读取静态构造函数底部设置的 _x 值。