如何防止方法跨多个线程运行?

我正在开发一个Web应用程序,其中有几个用户可以更新相同的记录。 因此,如果用户同时更新同一记录,以避免出现问题,我将其更改保存在队列中。 当每次保存发生时,我想调用一个在另一个线程上处理队列的方法,但我需要确保该方法在另一个线程中再次调用时不能运行。 我已经阅读了几篇关于这个主题的post,但不确定什么对我的情况最好。 以下是我现在的代码。 这是处理它的正确方法吗?

public static class Queue { static volatile bool isProcessing; static volatile object locker = new Object(); public static void Process() { lock (locker) { if (!isProcessing) { isProcessing = true; //Process Queue... isProcessing = false; } } } } 

新答案

如果要将这些记录持久保存到数据库(或数据文件或类似的持久性系统),则应让该底层系统处理同步。 正如JohnSaunders所指出的,数据库已经处理了同步更新。


鉴于您希望保留记录…… John提出的问题是您只是在Web应用程序的单个实例中同步对数据的访问。 尽管如此,可能会有多个实例同时运行(例如,在服务器场中,如果您拥有高流量,这可能是一个好主意)。 在这种情况下,使用队列来防止同时写入是不够的 ,因为在网页的多个实例之间仍然存在竞争条件。

在这种情况下,当您从不同的实例获得相同记录的更新时,底层系统无论如何都必须处理冲突,但由于更新的顺序已丢失,因此无法可靠地执行此操作。

除了这个问题,如果您将此数据结构用作缓存,那么它将提供不正确的数据,因为它不知道在另一个实例中发生的更新。


话虽如此,对于可能值得使用线程安全队列的场景。 对于这些情况,您可以使用ConcurrentQueue (正如我在原始答案的最后提到的那样)。

我会保留原来的答案,因为我看到了帮助理解.NET中可用的线程同步机制的价值(我预设了一些)。


原始答案

lock足以阻止多个线程同时访问代码段(这是互斥)。

在这里,我已经评论了你不需要的东西:

 public static class Queue { // static volatile bool isProcessing; static volatile object locker = new Object(); public static void Process() { lock (locker) { // if (!isProcessing) { // isProcessing = true; //Process Queue... // isProcessing = false; // } } } } 

话虽如此,尝试进入lock所有线程都将在队列中等待。 据我所知,这不是你想要的。 相反,你希望所有其他线程跳过块,只留下一个完成工作。 这可以通过Monitor.TryEnter完成:

 public static class Queue { static volatile object locker = new Object(); public static void Process() { bool lockWasTaken = false; try { if (Monitor.TryEnter(locker)) { lockWasTaken = true; //Process Queue... } } finally { if (lockWasTaken) { Monitor.Exit(locker); } } } } 

另一个好的选择是使用Interlocked :

 public static class Queue { static int status = 0; public static void Process() { bool lockWasTaken = false; try { lockWasTaken = Interlocked.CompareExchange(ref status, 1, 0) == 0; if (lockWasTaken) { //Process Queue... } } finally { if (lockWasTaken) { Thread.VolatileWrite(ref status, 0); } } } } 

无论如何,您不需要实现自己的线程安全队列。 你可以使用ConcurrentQueue 。