SemaphoreSlim.WaitAsync如何允许异步函数的顺序执行?

时间:2019-03-30 09:57:19

标签: c# multithreading asynchronous semaphore

此问题的上下文是WPF应用程序。 WPF应用程序使用DispatcherSynchronizationContext。

如果我的应用程序中有一个调用Button_Click处理程序方法的按钮,并且我想确保该函数中的所有代码仅由一个线程执行,我会将其包装为一个信号量,如图所示?但是我不明白这是如何工作的。

假设单击了该按钮,我们将点击WaitAsync(),它返回一个在输入信号量时完成的任务,所以我想马上吗?然后,我们将等待aa GetLengthAsync(),它将使我们跳回wpf消息循环。假设经过了10秒钟并再次单击了按钮,那么我们将再次进入Button_Click方法并单击WaitAsync(),这将返回一个在输入信号量时完成的任务,并且我们无法输入信号量,因此我们会跳回消息循环?是这样吗?

主要问题- 两次单击WaitAsync()时,我们都在同一线程上,并且信号量将并发性限制为一次仅允许一个线程执行该代码块,但也不允许同一线程输入该代码?信号量显然无法通过说其他一些线程(例如线程4或线程5)来获得,但即使再次通过我们的同一线程也无法获得?任何澄清将不胜感激。

private SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1,1);

public async void Button_Click(object sender, EventArgs args)
{
    await semaphoreSlim.WaitAsync();

    try
    {
        // GetLengthAsync takes 40 seconds to complete
        int length = await GetLengthAsync();

        // LongComputeFunc takes 30 seconds to complete
        int aggregate = LongComputeFunc(length);
    }
    finally
    {
        semaphoreSlim.Release();
    }
}

1 个答案:

答案 0 :(得分:0)

  

假设单击了该按钮,我们将点击WaitAsync(),它返回一个在输入信号量时完成的任务,所以我想马上吗?然后我们将等待aa GetLengthAsync(),它将使我们跳回wpf消息循环。

是的,是的。

  

假设经过了10秒钟,再次单击了按钮,那么我们将再次进入Button_Click方法并单击WaitAsync(),这将返回一个在输入信号量时完成的任务,并且我们无法输入信号量,因此我们反弹回到消息循环?是这样吗?

是的

  

主要问题-两次我们都单击WaitAsync()时,我们都在同一线程上,并且信号量将并发性限制为一次只允许一个线程执行该代码块,但不允许同一线程进入该线程代码吗?信号量显然无法通过其他线程(例如thread4或thread5)获得,但是即使再次由我们的同一线程获得也无法获得信号量?

是正确的。某些同步协调原语确实具有允许递归锁的功能(例如Monitor),而其他则不允许(例如Mutex)。但是,异步协调原语支持递归锁是不自然的。 (我个人是against recursive locking in general)。同步协调原语可以避免递归,因为存在“拥有”锁的“线程”概念。对于异步协调原语,不存在拥有锁的 thread 的概念。而是由“代码块”拥有锁。

因此,这是一种漫长的说法,即SemaphoreSlim.WaitAsync不是递归的(也不应该是递归的)。

现在,这是否是一个好的UX设计是另一个问题。如评论中所述,如果您一次只希望启动一个按钮,则UI disable 代表长时间运行的按钮更为常见。也就是说,如果您想 允许用户将多个长时间运行的操作排队,就可以使用SemaphoreSlim方法。在这种情况下,SemaphoreSlim就像是您的代码的一种隐式队列。

相关问题