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

use*_*438 5 c# multithreading

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

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;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

The*_*aot 14

新答案

如果要将这些记录持久保存到数据库(或数据文件或类似的持久性系统),则应让该底层系统处理同步.正如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;
            // }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

话虽如此,尝试进入的所有线程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);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

另一个好的选择是使用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);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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