C# 防止重入并确保为某些操作获取锁的正确方法是什么?

C# 防止重入并确保为某些操作获取锁的正确方法是什么?,c#,.net,multithreading,synchronization,C#,.net,Multithreading,Synchronization,我正在设计一个基类,当继承它时,它将针对多线程环境中的上下文提供业务功能。每个实例可能都有长时间运行的初始化操作,因此我希望使对象可重用。为此,我需要能够: 为其中一个对象指定一个上下文以允许它执行其工作 防止在对象已有新上下文时为其分配新上下文 防止在对象没有上下文时访问某些成员 此外,每个上下文对象可以由许多工作对象共享 是否有一个正确的同步原语适合我正在尝试做的事情?这是我想出的最适合我需要的模式: private Context currentContext; internal voi

我正在设计一个基类,当继承它时,它将针对多线程环境中的上下文提供业务功能。每个实例可能都有长时间运行的初始化操作,因此我希望使对象可重用。为此,我需要能够:

  • 为其中一个对象指定一个上下文以允许它执行其工作
  • 防止在对象已有新上下文时为其分配新上下文
  • 防止在对象没有上下文时访问某些成员
  • 此外,每个上下文对象可以由许多工作对象共享

    是否有一个正确的同步原语适合我正在尝试做的事情?这是我想出的最适合我需要的模式:

    private Context currentContext;
    
    internal void BeginProcess(Context currentContext)
    {
        // attempt to acquire a lock; throw if the lock is already acquired,
        // otherwise store the current context in the instance field
    }
    
    internal void EndProcess()
    {
        // release the lock and set the instance field to null
    }
    
    private void ThrowIfNotProcessing()
    {
        // throw if this method is called while there is no lock acquired
    }
    
    使用上述方法,我可以保护基类属性和方法,除非对象当前处于处理状态,否则不应访问这些属性和方法

    protected Context CurrentContext
    {
        get
        {
            this.ThrowIfNotProcessing();
            return this.context;
        }
    }
    
    protected void SomeAction()
    {
        this.ThrowIfNotProcessing();
    
        // do something important
    }
    

    我最初的想法是使用和相关函数,但这并不阻止相同的线程重新进入(在原始线程上多次调用
    BeginProcess

    在.NET中有一个同步对象不是可重入的,您正在寻找信号量

    在进行此操作之前,一定要把所有的事情都安排好,并问问自己,在同一个线程上如何能够再次调用BeginProcess()。这是非常非常不寻常的,您的代码必须是可重入的才能实现。这通常只能发生在具有dispatcher循环的线程上,GUI应用程序的UI线程就是一个常见的例子。如果这确实是可能的,并且您实际使用了一个信号量,那么您也将处理结果,您的代码将死锁。因为它递归到BeginProcess并在信号量上暂停。因此永远无法完成,也永远无法调用EndProcess()。监视器和互斥是可重入的有一个很好的理由:)

    您可以使用.NET Framework 2.0附带的类

    信号量的一个好用法是同步有限的资源量。在您的案例中,您似乎拥有希望在消费者之间共享的
    Context
    等资源

    您可以创建一个信号量来管理资源,如:

    var resourceManager = new Semaphore(0, 10);
    
    然后使用以下方法等待
    BeginProcess
    方法中的资源可用:

    resourceManager.WaitOne();
    
    resourceManager.Release();
    
    最后使用以下方法释放
    EndProcess
    方法中的资源:

    resourceManager.WaitOne();
    
    resourceManager.Release();
    
    下面是一个关于在类似您的情况下使用信号量的示例:


    有一种非常简单的方法可以防止重新进入(在一个线程上):
    private bool bRefresh=false;
    私有无效刷新()
    {
    如果(bRefresh)返回;
    bRefresh=真;
    尝试
    {
    //在这里做点什么
    }
    最后
    {
    bRefresh=false;
    }
    
    }
    你提出了好的观点;其主要功能是确保调用
    BeginProcess
    的基础结构代码正确执行。“一个好的程序员在过一条单行道之前会两全其美。”我不会让信号灯阻塞(我将调用
    WaitOne
    ,超时时间为零),并检查返回值以决定是否抛出。这将防止出现死锁情况。这个答案很有经验!这绝对不会阻止重入,因为
    if(bRefresh)返回
    bRefresh=true一起
    不是一个原子操作,因此当bRefresh为
    false
    时,多个线程可以计算条件,跳过
    返回
    ,然后继续将其设置为
    true
    并执行其余的工作。@Mike当然它确实防止了重复性-它只是不是线程安全的。这两个概念是完全不相关的——访问同一资源的两个线程与冗余无关。如果您需要防止来自多个线程的重复访问和同时访问,则需要将这两种方法结合起来,或使用同时考虑这两种方法的原语。您提供的链接已断开!既然您已经
    在锁已被获取时抛出,并且不想等待当前处理完成:为什么不在当前上下文不为null时抛出