我有两种方法MethodA
& MethodB
。 MethodB
必须在UI线程上运行。我需要他们一个接一个地运行而不允许MethodC
在它们之间运行。
MethodC
。
我采取了哪些措施来确保在代码周围添加Lock
:
Lock (MyLock)
{
MethodA(param1, param2);
MyDelegate del = new MyDelegate(MethodB);
if (this.IsHandleCreated) this.Invoke(del);
}
对于MethodC
:
public void MethodC()
Lock (MyLock)
{
Do bewildering stuff.....
}
问题是我卡住了。看起来我的代码陷入了僵局。
当我查看主题时,我发现按钮点击调用的代码停留在Lock (MyLock)
的{{1}},我的另一个主题似乎停留在MethodC
。
我已经读过在this.Invoke(del)
内调用方法很危险但是因为我是那个在那里编写代码的人,即使只有一个Lock
,这似乎也发生了。不是让我陷入麻烦的代码。
为什么Invoked方法会停止工作?
它是否可能等待Thread.Sleep
上的锁被释放,然后再回到它调用的原始锁?
答案 0 :(得分:7)
所以,想象一下以下情况:
您的后台线程开始运行代码。它抓住锁,然后开始运行MethodA
。
MethodC
处于工作中时,会调用 MethodA
。 MethodA
等待锁是免费的,阻止UI线程,直到发生这种情况。
后台线程完成MethodA
并在UI线程上调用MethodB
。在消息泵队列中的所有先前项目都已完成之前,MethodB
无法运行。
MethodC
位于消息泵队列的顶部,等待MethodB
完成,MethodB
在队列中等待MethodC
完成。他们都在互相等待,这是一个僵局。
那么,你如何解决这个问题呢?你真正需要的是一些“等待”锁而不实际阻塞线程的方法。幸运的是(在.NET 4.5中),由于Task Parallel Library,这很容易实现。 (我等待引号,因为我们实际上并不想等待,我们只想在释放锁定后立即执行MethodC
,而实际等待/阻止当前线程。)
而不是使用object
来MyLock
使用:
private static SemaphoreSlim semaphore = new SemaphoreSlim(1, 1);
现在MethodC
你可以这样做:
public async Task MethodC() //you can change the signature to return `void` if this is an event handler
{
try
{
await semaphore.WaitAsync();
//Do stuff
}
finally
{
semaphore.Release();
}
}
这里的关键是因为我们await
一个任务代表信号量实际上是免费的,我们没有阻止当前线程,这将允许其他后台任务将MethodB
编组到UI线程,完成方法,释放信号量,然后让这个方法执行。
您的其他代码不需要(但如果您愿意,仍然可以)在信号量上使用异步等待;阻止后台线程几乎没有问题,因此唯一的关键更改是使用信号量而不是lock
:
public void Bar()
{
try
{
semaphore.Wait();
MethodA(param1, param2);
MyDelegate del = new MyDelegate(MethodB);
if (this.IsHandleCreated) this.Invoke(del);
}
finally
{
semaphore.Release();
}
}
答案 1 :(得分:-1)
假设您的MethodA
还包含以下内容:
lock(MyLock) {
}
你是完全正确的,你已经死锁了。 MethodA
无法锁定MyLock
,因为在输入方法之前它已被锁定。
答案 2 :(得分:-1)
你可以试试这个:
Lock (MyLock)
{
MethodA(param1, param2);
MyDelegate del = new MyDelegate(MethodB);
MyDelegate del2 = new MyDelegate(MethodC);
MyDelegate del3 = del+del2
if (this.IsHandleCreated) this.Invoke(del3);
}
答案 3 :(得分:-1)
你混淆了使用锁的人。此任务与多线程本身无关。
简单的可用性解决方案就是您所需要的 - 在您准备好运行MethodC之前禁用可爱的小按钮。