这个操作线程安全吗?

fah*_*hmi 12 c# asp.net multithreading

在以下示例中,单击"提交"按钮时,静态变量Count的值将递增.但这个操作线程安全吗?是否正在使用Appliation对象进行此类操作?这些问题也适用于Web表单应用程序.

单击"提交"按钮时,计数似乎总是增加.

查看(剃刀):

@{
    Layout = null;
}
<html>

<body>
    <form>
        <p>@ViewBag.BeforeCount</p>
        <input type="submit" value="Submit" />
    </form>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

控制器:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        ViewBag.BeforeCount = StaticVariableTester.Count;
        StaticVariableTester.Count += 50;
        return View();
    }     
}
Run Code Online (Sandbox Code Playgroud)

静态类:

public class StaticVariableTester
{
    public static int Count;
}
Run Code Online (Sandbox Code Playgroud)

dca*_*tro 13

不,这不对.+ =运算符分3步完成:读取变量的值,将其增加1,分配新值.扩展:

var count = StaticVariableTester.Count;
count = count + 50;
StaticVariableTester.Count = count;
Run Code Online (Sandbox Code Playgroud)

线程可以在任何两个步骤之间被抢占.这意味着如果Count为0,并且两个线程+= 50同时执行,则可能Count是50而不是100.

  1. T1Count为0.
  2. T2Count为0
  3. T1 增加0 + 50
  4. T2 增加0 + 50
  5. T1 分配50到 Count
  6. T2 分配50到 Count
  7. 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();
}   
Run Code Online (Sandbox Code Playgroud)

或者使用 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();
} 
Run Code Online (Sandbox Code Playgroud)


dav*_*v_i 5

增量不是原子的,因此不是线程安全的.

退房Interlocked.Add:

添加两个32位整数,并将第一个整数替换为sum,作为原子操作.

你会这样使用它:

Interlocked.Add(ref StaticVariableTester.Count, 50);
Run Code Online (Sandbox Code Playgroud)

就个人而言,我会把它包装在你的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; }
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你想要返回的值(根据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;
}
Run Code Online (Sandbox Code Playgroud)

在您的代码中,您可以做到

ViewBag.BeforeCount = StaticVariableTester.AddAndGetOld(50);
Run Code Online (Sandbox Code Playgroud)

  • 除了不是线程安全的,在Web场的情况下,它不能跨服务器工作. (2认同)
  • @dcastro对于OP来说还算公平.MSDN说返回值是"存储在*location1*的新值". (2认同)