3

这是对我之前的一个问题的解释。这是一个简单的线程问题,但我似乎无法理解。

如果我有共享代码:

private static object objSync = new object();
private int a = 0;

    public void function()
    {
    lock(objSync)
      a += 2;

    lock(objSync)
      a += 3;
    Console.WriteLine(a.ToString());
    a=0;
    }

我不能期望 'a' 最终等于 5,因为第一个线程获得了一个锁,将 'a' 设置为 2,然后下一个线程可以在第一个线程能够之前获得一个锁,将其设置为 '4'加 3,最后你会得到 7。

据我了解,解决方案是在整个事情上加一个锁,然后你总是可以期待 5 个。现在我的问题是,如果在两个锁之间有一百万行代码怎么办。我无法想象将一百万行代码放在一起。您将如何通过在大约一百万两行代码中加锁来确保线程安全,但又不会导致性能失败?

编辑:

这是我为这个问题编写的废话代码。实际应用是线路监控系统。有两个屏幕显示当前线路,一个用于文员,一个用于公众。职员的屏幕通过串行端口接受“点击”,串行端口逐行推进,然后通过通知事件更新公共屏幕(参见观察者设计模式)。问题是,如果您向答题器发送垃圾邮件,它们不会同步。我想发生的事情是第一个屏幕添加到行号,显示它然后更新公共屏幕,但是在公共屏幕有机会显示数据库中的行号之前,店员再次点击,数字消失了同步。上面的“a”代表我从数据库中检索的一个值,而不仅仅是一个简单的 int。

4

2 回答 2

4

这完全取决于您对“成功”的定义。如果是计数器,那仍然可能是正确的结果。如果您只想更新它,如果它是您认为的那样,那么在第一个锁中拍摄快照,并与最后一个锁中的快照进行比较。在这种情况下,您可以用一些 Interlocked.CompareExchange 替换大部分代码。同样在“计数器”场景中,Interlocked.Increment 是您的朋友。

如果代码必须匹配,那么您有几个选择:

  • 如果比较失败,则再次运行整百万行;重复直到你赢得比赛(通过匹配)
  • 如果比较失败,则抛出异常
  • 阻止百万行;它可能仍然是快速的百万行......
  • 考虑一些对您的方案有意义的其他解决策略

在前两个中,您应该通过将任何相关数据置于中间状态来注意污染事物。

顺便说一句;最终分配给 0 可能应该是相同锁定策略的一部分。锁只有在所有访问都尊重它们的情况下才有效。

但是只有您的场景才能告诉我们冲突中的“正确”行为是什么。

于 2011-02-06T09:01:09.640 回答
0

这实际上是一个设计问题。如果您的方法根据特定情况为“a”计算不同的值,但您不希望另一个线程在操作处于活动状态时更改“a”的值,则更合适的是:

private static object objSync = new object();
private int a = 0;

    public void function()
    {
      int b;
      lock(objSync)
        b = a;
      b += 2;

      //million lines of code

      b +=3;
      lock(objSync)
      {
        a = b;
        Console.WriteLine(a.ToString());
        a=0;
      }
    }
于 2011-02-06T09:06:49.887 回答