在以下示例中,单击“提交”按钮时,静态变量Count的值将递增。但这个操作线程安全吗?是否正在使用Appliation对象进行此类操作?这些问题也适用于Web表单应用程序。
点击“提交”按钮后,计数似乎总是增加。
查看(剃刀):
@{
Layout = null;
}
<html>
<body>
<form>
<p>@ViewBag.BeforeCount</p>
<input type="submit" value="Submit" />
</form>
</body>
</html>
控制器:
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.BeforeCount = StaticVariableTester.Count;
StaticVariableTester.Count += 50;
return View();
}
}
静态类:
public class StaticVariableTester
{
public static int Count;
}
答案 0 :(得分:13)
不,不是。 + =运算符分3步完成:读取变量的值,将其增加1,分配新值。扩展:
var count = StaticVariableTester.Count;
count = count + 50;
StaticVariableTester.Count = count;
线程可以在任何两个步骤之间被抢占。这意味着如果Count
为0,并且两个线程同时执行+= 50
,则可能Count
将为50而不是100。
T1
将Count
读为0。T2
将Count
读为0 T1
添加0 + 50 T2
添加0 + 50 T1
将{50}分配给Count
T2
将{50}分配给Count
Count
等于50 此外,它也可以在前两个指令之间被抢占。这意味着两个并发线程可能将设置为ViewBag.BeforeCount
为0,只有然后增加StaticVariableTester.Count
。
使用锁
private readonly object _countLock = new object();
public ActionResult Index()
{
lock(_countLock)
{
ViewBag.BeforeCount = StaticVariableTester.Count;
StaticVariableTester.Count += 50;
}
return View();
}
或使用Interlocked.Add
public static class StaticVariableTester
{
private static int _count;
public static int Count
{
get { return _count; }
}
public static int IncrementCount(int value)
{
//increments and returns the old value of _count
return Interlocked.Add(ref _count, value) - value;
}
}
public ActionResult Index()
{
ViewBag.BeforeCount = StaticVariableTester.IncrementCount(50);
return View();
}
答案 1 :(得分:5)
增量不是原子的,因此不是线程安全的。
添加两个32位整数,并将第一个整数替换为和,作为原子操作。
你会这样使用它:
Interlocked.Add(ref StaticVariableTester.Count, 50);
我个人将这封在你的StaticVariableTester
课程中:
public class StaticVariableTester
{
private static int count;
public static void Add(int i)
{
Interlocked.Add(ref count, i);
}
public static int Count
{
get { return count; }
}
}
如果您想要返回的值(根据dcastro的评论),那么您可以随时执行:
public static int AddAndGetNew(int i)
{
return Interlocked.Add(ref count, i);
}
public static int AddAndGetOld(int i)
{
return Interlocked.Add(ref count, i) - i;
}
在你的代码中你可以做到
ViewBag.BeforeCount = StaticVariableTester.AddAndGetOld(50);
答案 2 :(得分:4)
如果方法(实例或静态)仅引用该方法中作用域的变量,那么它是线程安全的,因为每个线程都有自己的堆栈。您还可以使用各种同步机制来实现线程安全。
此操作不是线程安全的,因为它使用共享变量:ViewBag.BeforeCount。