通过创建一个静态实例,我有一个我想用作单例的类。当然,我也希望它是线程安全的。
假设我没有共享任何私人数据。但是,如果我没有弄错的话,仍然存在这样的问题:当调用静态对象实例的方法时,方法中的变量在线程之间共享,并且会产生不可预测的结果。
但是,在调用真正的静态方法时,会创建一个新的堆栈帧,因此无论如何它都是线程安全的(自身)。如果我没有弄错的话,再一次。
单身人士的这种模式是否是线程安全的?
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;
}
}
如果你想知道我为什么不首先创建一个静态类 - 我需要一个基于接口的单例,并且具有不同的实现,作为策略模式的一部分。这不适用于静态类。
答案 0 :(得分:5)
只要您的方法没有从实例方法或全局范围变量获取状态,您的方法就是可重入且线程安全的。它不一定是静态的。如下所示:
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()
方法不是线程安全的。
答案 1 :(得分:0)
如果我理解你的意思,那么你是对的。
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。
你唯一的危险就是如果你有私人领域。
方法中的变量是临时的,只有该方法可见调用。稍后或并发的方法调用会分别重新创建这些变量。
您唯一关心的是静态或实例字段。那些需要同步。
答案 2 :(得分:0)
您上面的代码是线程安全的,并且由于您指定的原因。我看到的问题是你没有实现单例。
您主要担心线程安全吗?如果是这样,线程安全通常适用于线程之间共享的对象实例。这意味着只要您不跨线程共享普通对象或在类级别创建静态数据,您应该没问题。
我正在添加一个使用单例和接口的例子,有或没有工厂。注意:我没有运行此代码。
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;
}
}