此操作线程是否安全
本文关键字:安全 是否 线程 操作 | 更新日期: 2023-09-27 17:56:19
在下面的示例中,当单击"提交"按钮时,静态变量 Count 的值将递增。但是此操作线程安全吗?使用应用程序对象是执行此操作的正确方法吗?这些问题也适用于 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;
}
不,不是。+= 运算符分 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();
}
增量
不是原子的,所以不是线程安全的。
退房Interlocked.Add
:
将两个 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);
如果一个方法(实例或静态)只引用该方法范围内的变量,那么它是线程安全的,因为每个线程都有自己的堆栈。还可以通过使用各种同步机制来实现线程安全。
此操作不是线程安全的,因为它使用共享变量:ViewBag.BeforeCount。
是什么使方法具有线程安全性?规则是什么?