数据完整性
总体而言,在数据完整性方面,你要担心的问题是竞争条件和死锁。多个线程试图在同一时间更新相同的对象就会造成竞争条件,这将招致麻烦。想象一下如果使用下面这段代码:
以下为引用的内容:
int x = 5;
x = x + 10;
x = x + 10;
现在,如果线程A和线程B在同一时间运行此代码,将会发生什么情况?它可以运行得很好?还是会出现什么问题?如果出现问题,又是些怎样的问题呢?每个线程都不会一次执行全部语句。因此,我们可以按照以下顺序操作:
以下为引用的内容:
1. Thread A retrieves the value of x (5).
2. Thread B retrieves the value of x (5).
3. Thread A assigns x + 10 (15) to x.
4. Thread B assigns x + 10 (15) to x.
5. x is now equal to 15.
2. Thread B retrieves the value of x (5).
3. Thread A assigns x + 10 (15) to x.
4. Thread B assigns x + 10 (15) to x.
5. x is now equal to 15.
或者,相同的代码可以按照不同的顺序:
以下为引用的内容:
1. Thread A retrieves the value of x (5).
2. Thread A assigned x = 10 (15) to x.
3. Thread B retrieves the value of x (15).
4. Thread B assigns x + 10 (25) to x.
5. x is now equal to 25.
2. Thread A assigned x = 10 (15) to x.
3. Thread B retrieves the value of x (15).
4. Thread B assigns x + 10 (25) to x.
5. x is now equal to 25.
在.NET架构中,最简单也最常见的解决竞争条件的方法是使用“临界区”。而在VB.NET中,该语句是“加锁”,并在C#中是“锁定”,这两种语句都是把对象作为参数。其他尝试锁定相同对象实例使用的临界区(包括上文所指的)会阻止运行直到锁定解除,这样每次就只有一个临界区运行。我们先前举例的一段代码现在看起来是这样的:
以下为引用的内容:
int x = 5;
object lockObject = new object();
Monitor.Enter(lockObject);
x = x + 10;
Monitor.Exit(lockObject);
object lockObject = new object();
Monitor.Enter(lockObject);
x = x + 10;
Monitor.Exit(lockObject);
什么是监控器可以提供而临界区做不到的呢?答案是没有。除非你在解锁后需要更细粒度的控制权。有些复杂的代码可能需要锁定或长或短的一段时间,这都取决于运行的情况,比方一个变量的值。在这种情况下,选择监控器要比需选择临界区更合适。
另一个值得关注的有关数据完整性的问题是死锁。当多个线程锁定资源导致它们都不能够继续运行时,就会出现死锁。例如:
以下为引用的内容:
Thread A:
Monitor.Enter(object1);
Monitor.Enter(object2);
//Do work
Monitor.Exit(object1);
Monitor.Exit(object2);
Thread B:
Monitor.Enter(object2);
Monitor.Enter(object1);
//Do work
Monitor.Exit(object1);
Monitor.Exit(object2);
Monitor.Enter(object1);
Monitor.Enter(object2);
//Do work
Monitor.Exit(object1);
Monitor.Exit(object2);
Thread B:
Monitor.Enter(object2);
Monitor.Enter(object1);
//Do work
Monitor.Exit(object1);
Monitor.Exit(object2);
如果线程A和线程B都调用它们的第一段语句并且同时完成运行,那它们都无法调用它们的第二段语句——这就是一个死锁。所以编写代码的时候细心,要仔细想清楚怎样编写代码才更有利。死锁的发生常见于新手,因为他们过分设置锁定把它变得太详细了。如果代码被嵌套锁定通常表明需要对编写的代码加以认真检查。