Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何防止方法跨多个线程运行?_C#_Multithreading - Fatal编程技术网

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

C# 如何防止方法跨多个线程运行?,c#,multithreading,C#,Multithreading,我正在开发一个web应用程序,其中多个用户可以更新相同的记录。因此,为了避免用户同时更新同一记录时出现问题,我将他们的更改保存在队列中。每次保存时,我想调用一个在另一个线程上处理队列的方法,但我需要确保如果再次调用该方法,该方法将无法在另一个线程中运行。我已经读了好几篇关于这个主题的帖子,但不确定什么最适合我的情况。下面是我现在的代码。这是正确的处理方法吗 public static class Queue { static volatile bool isProcessing;

我正在开发一个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;
            }
        }
    }
}

新答案

如果要将这些记录持久化到数据库(或数据文件或类似的持久化系统),则应该让底层系统处理同步。因为数据库已经处理同步更新


如果你想保留这些记录。。。问题是,您只同步对web应用程序的单个实例中的数据的访问。不过,可能有多个实例同时运行(例如,在服务器场中,如果流量很大,这可能是个好主意)。在这种情况下,使用队列来防止同时写入是不够的,因为网页的多个实例之间仍然存在竞争条件

在这种情况下,当您从不同实例获取同一记录的更新时,参考底图系统无论如何都必须处理冲突,但由于更新顺序已丢失,因此无法可靠地处理冲突

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


尽管如此,对于可能值得使用线程安全队列的场景。对于这些情况,您可以使用(正如我在原始答案末尾提到的)

我将保留我最初的答案,因为我认为有助于理解.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;
            // }
        }
    }
}

话虽如此,所有试图进入
锁的线程都将在队列中等待。据我所知,这不是你想要的。相反,您希望所有其他线程跳过该块,只留下一个线程执行该工作。这可以通过以下方式实现:


另一个好的选择是使用:



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

-1:在web应用程序中不够好。在回收或web场或web花园的情况下,可能有多个AppDomain副本在运行。@JohnSaunders您是在告诉我多个AppDomain共享静态字段吗?不,我是在告诉您,可能有多个AppDomain同时运行同一代码,所以静态域的多个副本,这意味着多个AppDomain之间没有同步。@johnsa理解是的,因为它不是相同的数据。尝试在这个级别同步它们是没有意义的。如果我们正在处理需要跨AppDomains保存的数据,那么——正如您所建议的那样——使用数据库是一个更好的主意。我不确定这是否是用户2628438想要的。为什么数据会不一样呢?它是同一个web应用程序,可能是同一组用户。数据库知道如何避免同时更新问题。为什么要重新发明轮子?数据存储在Azure表中。在原始记录请求和更新之间,处理可能需要一些时间。如果另一个用户在之后进行了更新,则存储客户端将抛出一个错误。@user2628438我已更新了我的答案以确保中止安全。这意味着,如果具有锁的线程被中止(您无论如何都不应该这样做),另一个线程可以进入。我还修复了第二个示例中的一个bug(如果
TryEnter
成功,我必须调用
Exit
来完成关键部分)。注意:
lock
自C#4.0以来是中止安全的。@Theraot,
监视器.TryEnter
示例中的
\u syncroot
对象是什么?或者我应该使用那里的
locker
对象吗?@user2628438是的,那将是
locker
。我已经更新了
syncroot
Monitor
中使用的对象的通用名称,因为当Microsoft认为公开对象是个好主意时,他们就是这样称呼的。您仍然可以找到公开
SyncRoot
的类,这些类来自.NET 2.0或更早版本(它是
ICollection
接口的一部分,不是泛型接口)。您是否应该使用这些类公开的
SyncRoot
?不
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);
            }
        }
    }
}
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);
            }
        }
    }
}