从Lock()内的UI线程调用方法

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被释放后再返回到它所调用的原始锁定?

Ser*_*rvy 8

那么,想象一下以下情况:

  1. 您的后台线程开始运行代码.它抓住锁,然后开始运行MethodA.

  2. MethodCMethodA正在工作的过程中被调用. MethodA等待锁是免费​​的,阻止UI线程,直到发生这种情况.

  3. 后台线程完成MethodA并转到MethodBUI线程上调用. MethodB消息泵队列中的所有先前项目都已完成后才能运行.

  4. MethodC位于消息泵队列的顶部,等待直到MethodB完成,并MethodB在队列中等待直到MethodC完成.他们都在互相等待,这是一个僵局.

那么,你如何解决这个问题呢?你真正需要的是一些"等待"锁而不实际阻塞线程的方法.幸运的是(在.NET 4.5中),由于Task Parallel Library,这很容易实现.(我在引号中等待,因为我们实际上并不想等待,我们只想MethodC在释放锁定后立即执行而不实际等待/阻塞当前线程.)

而不是使用objectMyLock用途:

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)