我正在为ASP.NET缓存项目删除事件创建一个回调函数。
文档说我应该调用一个对象或调用我知道将存在的调用(将在范围内),例如静态方法,但它说我需要确保静态是线程安全的。
第1部分:我可以采取哪些措施来使其成为非线程安全的?
第2部分:这是否意味着如果我有
static int addOne(int someNumber){
int foo = someNumber;
return foo +1;
}
我打电话给Class.addOne(5);和Class.addOne(6);同时,我可能会返回6或7,具体取决于谁首先调用foo? (即竞争条件)
答案 0 :(得分:52)
addOne 函数确实是线程安全的,因为它不访问任何可由另一个线程访问的数据。局部变量不能在线程之间共享,因为每个线程都有自己的堆栈。但是,您必须确保函数参数是值类型而不是引用类型。
static void MyFunction(int x) { ... } // thread safe. The int is copied onto the local stack.
static void MyFunction(Object o) { ... } // Not thread safe. Since o is a reference type, it might be shared among multiple threads.
答案 1 :(得分:38)
不,addOne在这里是线程安全的 - 它只使用局部变量。这是一个不会是线程安全的示例:
class BadCounter
{
private static int counter;
public static int Increment()
{
int temp = counter;
temp++;
counter = temp;
return counter;
}
}
这里,两个线程可以同时调用Increment,最后只增加一次。
(顺便说一句,使用return ++counter;
同样糟糕 - 上面是同一件事的更明确的版本。我扩展它以便更明显是错误的。)
什么是线程安全的细节可能非常棘手,但总的来说如果你没有改变任何状态(除了传递给你的东西,无论如何 - 那里有一些灰色区域)然后它通常没关系。
答案 2 :(得分:22)
答案 3 :(得分:7)
你的方法很好,因为它只使用局部变量,让我们稍微改变你的方法:
static int foo;
static int addOne(int someNumber)
{
foo=someNumber;
return foo++;
}
这不是一个线程安全的方法,因为我们正在触摸静态数据。然后需要将其修改为:
static int foo;
static object addOneLocker=new object();
static int addOne(int someNumber)
{
int myCalc;
lock(addOneLocker)
{
foo=someNumber;
myCalc= foo++;
}
return myCalc;
}
我认为这是一个愚蠢的样本,如果我正确地读它就会导致foo没有任何意义,但是嘿,这是一个样本。
答案 4 :(得分:3)
如果它正在修改函数外部的某个变量,那么这只是一个竞争条件。你的例子不是那样做的。
这基本上就是你所期待的。线程安全意味着该功能:
外部数据可以是存储(数据库/文件)中的东西,也可以是应用程序内部的东西(变量,类的实例等):基本上在世界任何地方声明的任何东西都在功能的范围。
你的函数的非线程安全版本的一个简单例子是:
private int myVar = 0;
private void addOne(int someNumber)
{
myVar += someNumber;
}
如果你在没有同步的情况下从两个不同的线程调用它,查询myVar的值将会有所不同,具体取决于在对addOne的所有调用完成后查询是否发生,或者查询是否发生在两次调用之间,或查询是否发生在任何一个电话之前。
答案 5 :(得分:3)
有一些研究允许您检测非线程安全的代码。例如。项目CHESS at Microsoft Research。
答案 6 :(得分:2)
在上面的例子中。
线程安全主要与存储状态有关。您可以通过执行以下操作使上面的示例非线程安全:
static int myInt;
static int addOne(int someNumber){
myInt = someNumber;
return myInt +1;
}
这意味着由于上下文切换线程1可能会调用myInt = someNumber然后上下文切换,让我们说线程1只是将其设置为5.然后想象线程2进来并使用6并返回7。然后当线程1再次唤醒时,它将在myInt中有6而不是它正在使用的5并且返回7而不是预期的6.:O
答案 7 :(得分:1)
在任何地方,thread safe表示在访问资源时没有两个或多个线程发生冲突。通常静态变量 - 用C#,VB.NET和Java等语言 - 使你的代码线程不安全。
在Java中存在 synchronized 关键字。但是在.NET中你得到汇编选项/指令:
class Foo
{
[MethodImpl(MethodImplOptions.Synchronized)]
public void Bar(object obj)
{
// do something...
}
}
非线程安全类的示例应该是单例,具体取决于此模式的编码方式。通常它必须实现 synchronized 实例创建者。
如果您不想使用 synchronized方法,可以尝试使用锁定方法,例如 spin-lock 。
答案 8 :(得分:0)
对两个线程可以同时使用的对象的任何访问都不是线程安全的。
您在第2部分中的示例显然是安全的,因为它仅使用作为参数传入的值,但如果您使用了对象范围变量,则可能必须使用适当的锁定语句来包围访问
答案 9 :(得分:0)
foo
,因此addOne
是线程安全的。
答案 10 :(得分:0)
'foo'和'someNumber'在你的例子中是安全的原因是它们驻留在堆栈上,每个线程都有自己的堆栈,因此不会共享。
只要数据有可能被共享,例如,全局或共享指向对象的指针,那么您可能会遇到冲突,并且可能需要使用某种类型的锁。