这是对我以前的一个问题的解释。这是一个简单的线程问题,但我似乎无法理解它。
如果我有共享代码:
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'表示我从db检索的值,而不仅仅是一个简单的int。有一些地方我改变了价值,我需要所有这些以原子方式发生。
答案 0 :(得分:4)
这一切都取决于你所定义的“成功”。如果它是一个计数器,那可能仍然是正确的结果。如果您只是想要更新它,那么在想到的情况下更新它,然后在第一个锁内部拍摄快照,同样与最终锁中的快照进行比较。在那种情况下,您可以使用Interlocked.CompareExchange替换大部分代码。同样在“计数器”场景中,Interlocked.Increment是你的朋友。
如果代码必须匹配,那么您有几个选项:
在前两个中,您应该通过将任何相关数据保留在中间状态来监视污染事物。
顺便说一下;最终赋值为0应该也是同一个锁定策略的一部分。锁只有在所有访问都遵守它们的情况下才有效。
但只有您的情景可以告诉我们冲突中的“正确”行为是什么。
答案 1 :(得分: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;
}
}