E.T*_*.T. 4 c# multithreading locking invoke
我有两种方法,MethodA&MethodB.MethodB必须在UI线程上运行.我需要它们一个接一个地运行而不允许MethodC它们运行.
MethodC 当用户点击一个可爱的小按钮时调用.
我做了什么来确保这是Lock围绕代码:
lock (MyLock)
{
MethodA(param1, param2);
MyDelegate del = new MyDelegate(MethodB);
if (this.IsHandleCreated) this.Invoke(del);
}
Run Code Online (Sandbox Code Playgroud)
并为MethodC:
public void MethodC()
lock (MyLock)
{
Do bewildering stuff.....
}
Run Code Online (Sandbox Code Playgroud)
问题是我卡住了.看起来我的代码陷入了僵局.
当我看到线程时,我看到按钮点击调用的代码被卡住lock (MyLock)了MethodC,我的另一个线程似乎陷入了困境this.Invoke(del).
我已经读过从内部调用一个方法是危险的,Lock但是因为我是那个在那里编写代码的人,这似乎发生了,即使只是Thread.Sleep我认为这不是代码让我陷入困境.
为什么Invoked方法会停止工作?它是否可能等待锁定methodC被释放后再返回到它所调用的原始锁定?
那么,想象一下以下情况:
您的后台线程开始运行代码.它抓住锁,然后开始运行MethodA.
MethodC在MethodA正在工作的过程中被调用. MethodA等待锁是免费的,阻止UI线程,直到发生这种情况.
后台线程完成MethodA并转到MethodBUI线程上调用. MethodB消息泵队列中的所有先前项目都已完成后才能运行.
MethodC位于消息泵队列的顶部,等待直到MethodB完成,并MethodB在队列中等待直到MethodC完成.他们都在互相等待,这是一个僵局.
那么,你如何解决这个问题呢?你真正需要的是一些"等待"锁而不实际阻塞线程的方法.幸运的是(在.NET 4.5中),由于Task Parallel Library,这很容易实现.(我在引号中等待,因为我们实际上并不想等待,我们只想MethodC在释放锁定后立即执行而不实际等待/阻塞当前线程.)
而不是使用object的MyLock用途:
private static SemaphoreSlim semaphore = new SemaphoreSlim(1, 1);
Run Code Online (Sandbox Code Playgroud)
现在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();
}
}
Run Code Online (Sandbox Code Playgroud)
这里的关键是因为我们的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();
}
}
Run Code Online (Sandbox Code Playgroud)