老问题,我知道,但是我想把它扔给那些绊倒它的人。
C#(大概是VB.NET)为处理线程同步提供了一些不错的选择。您可以使用lock
关键字来阻止执行,直到给定锁定可用;或者,如果要指定用于取得锁定的超时(可能立即),则使用Monitor.TryEnter()
。
对于这些方法中的任何一种,都需要一个用于锁定的对象。几乎任何对象都会做;如果你不同步访问某个对象本身(集合,数据库连接,无论),你甚至可以实例化一次性的object
。对于轮询定时器,后者是典型的。
首先,确保你有一个对象用于同步:现在
public class DatabasePollingClass {
object PollingTimerLock = new object();
...
,如果你想轮询线程阻塞无限期地等待轮到自己,用lock
关键字:
public class DatabasePollingClass {
object PollingTimerLock = new object();
...
protected void PollingTimerCallback() {
lock (PollingTimerLock) {
//Useful stuff here
}
}
}
一次只允许一个线程在lock (PollingTimerLock)
代码块内。所有其他线程将无限期地等待,然后一旦它们可以获取自己的锁,就可以继续执行。
但是,你可能不希望这种行为。如果您希望后续线程立即中止(或稍后等待),如果另一个轮询线程仍在运行,则在锁定时可以使用Monitor.TryEnter()
。这确实需要稍微谨慎,但是:
public class DatabasePollingClass {
object PollingTimerLock = new object();
...
protected void PollingTimerCallback() {
if (Monitor.TryEnter(PollingTimerLock)) { //Acquires lock on PollingTimerLock object
try {
//Useful stuff here
} finally {
//Releases lock.
//You MUST do this in a finally block! (See below.)
Monitor.Exit(PollingTimerLock);
}
} else {
Console.WriteLine("Warning: Polling timer overlap. Skipping.");
}
}
}
额外小心从不像lock
关键字,Monitor.TryEnter()
需要你,当你用它完成手动解除锁定的事实造成的。为了确保发生这种情况,您需要将整个关键部分包装在try
区块中,并释放finally
区块中的锁定。这是为了确保锁定将被释放,即使轮询方法失败或提前返回。如果该方法在未释放锁的情况下返回,则您的程序将被有效挂起,因为没有其他线程能够获取该锁。
另一个不使用锁定机制的选项是配置你的定时器而不需要重复周期,即一次性定时器。在轮询方法结束时,您将丢弃旧的定时器,并设置一个新的定时器(您还需要在finally
块内执行此操作,以确保定时器在方法结束时被重置)。如果您想要在先前轮询的结束以来的某个时间间隔轮询数据库,此方法将非常有用。这是一个微妙的区别,但它也解决了并发轮询尝试的问题。
请注意,这是一个真的简单线程并发示例。只要所有的锁定都是在与UI线程分开的线程上发生的(消息泵本身可能成为争用的焦点),并且您只锁定了一个对象,那么您不必过多担心死锁。那些调试真的很不愉快;症状通常是“应用程序停止响应,现在您可以猜测哪些线程正在等待什么”。
来源
2014-03-19 14:13:36
db2
依靠数据库和网络锁定的问题是什么?锁定记录的最佳方式是锁定记录。 – Paparazzi