C# 我想知道有没有更好的方法来实现这一点。”;“简易锁”;
有没有更好的方法实现下面这样一个简单的锁 我只想在“DOSOMETHING”还没有运行的情况下使用它。我应该在这里使用reall锁吗?如果我使用锁,会导致所有东西排队等待锁释放吗?(这不是我想要的!) 谢谢C# 我想知道有没有更好的方法来实现这一点。”;“简易锁”;,c#,locking,nonblocking,C#,Locking,Nonblocking,有没有更好的方法实现下面这样一个简单的锁 我只想在“DOSOMETHING”还没有运行的情况下使用它。我应该在这里使用reall锁吗?如果我使用锁,会导致所有东西排队等待锁释放吗?(这不是我想要的!) 谢谢 bool running = false; void DataDisplayView_Paint(object sender, PaintEventArgs e) { // if (!this.initialSetDone) if (!running)
bool running = false;
void DataDisplayView_Paint(object sender, PaintEventArgs e)
{
// if (!this.initialSetDone)
if (!running)
{
this.running = true;
//DOSOMETHING
this.running = false;
}
}
最好的方法是使用try/finally块
try {
this.running = true;
...
} finally {
this.running = false;
}
只有从多个线程调用此方法时,才需要真正的线程锁。鉴于它似乎是一个绘制事件处理程序,这不太可能,因为控件与单个线程密切相关。否,您不希望在此处使用锁。这不是线程同步问题。这是一个方法可重入性问题 你可以试试这样的
bool running = false;
void DataDisplayView_Paint(object sender, PaintEventArgs e)
{
if (!this.running)
{
this.running = true;
try
{
//DOSOMETHING
}
finally
{
this.running = false;
}
}
}
您只需同步(锁定是最简单的方式)代码位:
bool running = false;
readonly object padlock = new object();
void DataDisplayView_Paint(object sender, PaintEventArgs e)
{
if (!this.initialSetDone)
{
lock(padlock)
{
if(running) return;
running = true;
}
try {
//DOSOMETHING
}
finally
{
lock(padlock)
{
this.running = false;
}
}
}
}
如果改用Monitor.TryEnter,则可以指定超时,在这种情况下,得到的结果如下:
- 一次只能有一个线程运行DOSOMETHING
- 后续调用将尝试获取锁并在超时子句之后放弃
running
变量设置为volatile,以便始终获得存储在变量中的最新值:
private volatile bool running;
if (!this.initialSetDone && !this.running) // #1
{
this.running = true;
try
{
// DOSOMETHING
}
finally
{
this.running = false;
}
}
第二种方法不会排队后续调用,但有可能两个线程都会命中1,并评估它是安全进行的,然后两个都结束运行,虽然这是极不可能的。
< P>你在中间移动可变名称,所以我假设你想要: bool running = false;
void DataDisplayView_Paint(object sender, PaintEventArgs e)
{
if (!this.running)
{
this.running = true;
//DOSOMETHING
this.running = false;
}
}
这里的问题是,如果可以从多个线程调用DataDisplayView_Paint,则可能在
if(!this.running)
和this.running=true之间代码>另一个线程可以跳入并启动DOSOMETHING(因为running仍然是false)。然后,第一个线程将继续,并再次开始DOSOMETHING。如果这是可能的,那么您将需要使用真正的锁。我遗漏了什么吗?您发布的代码似乎没有任何作用。也就是说,无论running
是否为true,代码都将运行
通常,任何试图像这样“锁定”自身的代码
if (!running)
{
running = true;
try
{
// This code should not call itself recursively.
// However, it may execute simultaneously on more than one thread
// in very rare cases.
}
finally
{
running = false;
}
}
…非常好,只要您处于单线程场景中。如果正在运行多线程代码,则可能会出现问题,因为您假设没有两个线程同时到达If(!running)
行
多线程代码中的解决方案是使用某种形式的原子开关。为此,我使用了AutoResetEvent
:
var ready = new AutoResetEvent(true);
if (ready.WaitOne(0))
{
try
{
// This code will never be running on more than one thread
// at a time.
}
finally
{
ready.Set();
}
}
请注意,如果在绘制回调中有重入,则会出现更严重的问题。绘制处理程序应该阻塞消息泵(并且应该相对快速地完成),所以您永远不会看到这种情况。唯一的例外是,如果您从paint处理程序中的某个位置调用Application.DoEvents(),您确实不应该这样做
我只想去“DOSOMETHING”如果
它还没有运行
您的问题没有足够的信息,因此我不得不对您的代码进行假设
- 我的第一个假设是,基于签名
DataDisplayView\u Paint(objects s,PaintEventArgs e)
,您的代码在GUI线程上运行
- 我的第二个假设是,您的代码
DOSOMETHING
是同步的
记住这一点,下面是您的代码版本,它保证我们只运行DOSOMETHING
,如果它还没有运行:
void DataDisplayView_Paint(object s, PaintEventArgs e)
{
//DOSOMETHING
}
GUI线程一次只处理一条消息,DataDisplayView\u Paint
方法在DOSOMETHING
完成之前不会退出。如果您正在使用GUI执行任何操作,如绘制图形对象或更改标签,那么此代码将不会从多个线程调用——如果调用,则,.NET将抛出异常。换句话说,您不需要任何同步
让我们假设DOSOMETHING
异步运行——现在我们有一个有趣的问题,但它很容易解决,并且不需要任何布尔运算
实际上,您所做的只是在DOSOMETHING运行时禁用事件处理程序,然后重新启用它。不使用bool,而是根据需要取消钩住并重新钩住事件处理程序:
void DataDisplayView_Paint(object s, PaintEventArgs e)
{
DataDisplayView.Paint -= DataDisplayView_Paint;
DoSomethingAsynchronously(); // re-hooks event handler when completed
}
void DoSomethingAsychronously()
{
ThreadPool.QueueUserWorkItem(() =>
{
try
{
// DOSOMETHING
}
finally
{
// may need a lock around this statement
DataDisplayView.Paint += DataDisplayView_Paint;
}
});
}
DOSOMETHING方法倾向于抛出;)@grzenio,这会导致排队等待挂锁释放吗?@AidanO,你只需要锁来确保检查和分配或运行是原子化的,不会有任何真正的等待。我已经读了你的问题很多次了,根本不清楚你想做什么。你能提供一些背景资料或更完整的描述你的代码应该做什么吗?到目前为止,所有的答案通常存在于两个阵营中的一个阵营:1)你在使用多个线程,你在描述一个线程同步问题;2)你没有使用多个线程,你在描述一个方法可重入性问题。你能通过更新来澄清你的问题吗?根据我目前看到的情况,我假设是后者。是这样吗?@Juliet,对不起,不正确地使用“initialSetDone”而不是“running”混淆了这个问题@布莱恩,是的,这是一个重入问题。答案似乎表明我使用的方法是正确的,但是可以通过使用try-catch来改进。问题是,在第一个线程成功设置running=true之前,两个线程可以通过检查if(!running)。在本例中,您将看到两个线程同时运行DOSOMETHING。@Grzenio:True,但问题从未表明涉及的多个线程和方法的签名
void DataDisplayView_Paint(object s, PaintEventArgs e)
{
DataDisplayView.Paint -= DataDisplayView_Paint;
DoSomethingAsynchronously(); // re-hooks event handler when completed
}
void DoSomethingAsychronously()
{
ThreadPool.QueueUserWorkItem(() =>
{
try
{
// DOSOMETHING
}
finally
{
// may need a lock around this statement
DataDisplayView.Paint += DataDisplayView_Paint;
}
});
}