通过使用私有静态方法使单例线程安全
本文关键字:单例 线程 静态方法 安全 | 更新日期: 2023-09-27 18:04:04
我有一个类,我想通过创建它的静态实例来作为单例使用。当然,我也希望它是线程安全的。
假设我没有共享任何私人数据。但是,如果我没有弄错的话,在调用静态对象实例的方法时,仍然存在一个问题,方法中的变量在线程之间共享,并且会产生不可预测的结果。
然而,当调用一个真正的静态方法时,会创建一个新的堆栈帧,因此它无论如何都是线程安全的。再说一遍,如果我没弄错的话。
单例模式是线程安全的吗?
class Singleton
{
public object SomeMethod(object arg) {
return Singleton.SomeMethodImpl(arg);
}
private static object SomeMethodImpl(object arg) {
// is in unique stack frame?
object Result;
...
return Result;
}
}
如果你想知道为什么我不首先创建一个静态类——我需要一个基于接口的单例,并且有不同的实现,作为策略模式的一部分。
只要你的方法不是从实例方法或全局作用域变量获取状态,你的方法就是可重入的和线程安全的。它不一定是静态的。比如:
int AddTwo(int a, int b)
{
return a + b;
}
这是完全线程安全的,可以随意调用。即使在方法内部定义变量也是可以的,只要它们是方法之间共享的实例变量。
方法,如:
string ReverseString(string s)
{
char[] charArray = s.ToCharArray();
Array.Reverse( charArray );
return new string( charArray );
}
上面的方法也是可重入的,是线程安全的。
一旦你开始添加变量,无论它们是静态的还是来自不同作用域的实例,你就会开始遇到线程安全问题。
class BadExample
{
private int counter;
private void IncrementCounter()
{
++counter;
}
}
在上面的例子中,IncrementCounter()
方法不是线程安全的。 如果我明白你的意思,那么你是对的。
object Result; // this is on its unique stack frame and is safe so far
Result = new ... // creating something on the heap that Result points to
// still safe because it's the only reference to it
即使多个线程调用此方法,它们也会在堆上创建不同的新变量,并将它们赋值给不同堆栈的Result。
唯一的危险是如果你有私有字段。
方法中的变量是临时的,并且只对方法调用可见。以后或并发方法调用分别重新创建这些变量。
您唯一关心的是静态或实例字段。
上面的代码是线程安全的,并且由于您指定的原因。我看到的问题是你没有实现单例。
你主要担心线程安全吗?如果是这样,线程安全通常适用于线程之间共享的对象实例。这意味着,只要你不跨线程共享一个普通对象或在类级别创建静态数据,你应该是ok的。
我正在添加一个使用带有接口的单例,带和不带工厂。注意:我没有运行这段代码。
public interface ISomething
{
void Method();
}
public class Class1 : ISomething
{
public void Method()
{
throw new NotImplementedException();
}
}
public class Class2 : ISomething
{
public void Method()
{
throw new NotImplementedException();
}
}
public class Singleton
{
private static ISomething privateObject;
public static ISomething Instance()
{
lock (privateObject)
{
if (privateObject == null)
{
privateObject = new Class1();
}
}
return privateObject;
}
}
public class SingletonUsingFactory
{
private static ISomething privateObject;
public static ISomething Instance(int param)
{
lock (privateObject)
{
if (privateObject == null)
{
privateObject = FactoryClass.CreationObject(param);
}
}
return privateObject;
}
}
public static class FactoryClass
{
public static ISomething CreationObject(int whatToCreate)
{
ISomething createdObject;
switch (whatToCreate)
{
case 0:
createdObject = new Class1();
break;
case 1:
createdObject = new Class2();
break;
default:
throw new Exception();
}
return createdObject;
}
}