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时抛出?