SemaphoreSlim.WaitAsync 如何允许顺序执行异步函数?
How does SemaphoreSlim.WaitAsync allow for sequential executions of an async function?
这个问题的上下文是一个 WPF 应用程序。 WPF 应用程序使用 DispatcherSynchronizationContext。
如果我的应用程序中有一个调用 Button_Click 处理程序方法的按钮,并且我想确保该函数中的所有代码仅由一个线程执行,我会将其包装在一个信号量中,如图所示?但我不明白这是如何工作的。
假设按钮被点击,我们会点击 WaitAsync() ,returns 一个任务在输入信号量时完成,所以我猜是立即?然后我们将点击 await GetLengthAsync() 这将使我们跳回 wpf 消息循环。假设 10 秒过去,再次点击按钮,那么我们将再次进入 Button_Click 方法并点击 WaitAsync(),这 returns 当我们进入信号量时完成的任务,我们无法进入信号量让我们跳回消息循环?是这样吗?
主要问题 -
我们两次点击 WaitAsync() 我们都在同一个线程上,我们的信号量将并发限制为一次只允许一个线程执行该代码块,但它也不允许我们的同一个线程输入该代码吗?信号量显然不能通过其他线程(如 thread4 或 thread5)获取,但即使是我们的同一个线程也无法再次获取?任何澄清将不胜感激。
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();
}
}
Assuming the button was clicked, we would hit WaitAsync() which returns a task that completes when the semaphore is entered, so I guess immediately? Then we would hit await GetLengthAsync() which would bounce us back out to the wpf message loop.
是的,是的。
Assuming 10 seconds goes by and the button is clicked again, then we would enter the Button_Click method again and hit WaitAsync(), which returns a task that completes when we enter the semaphore, and we cant enter the semaphore so we bounce back out to the message loop? is that how it works?
是的。
MAIN QUESTION - Both times we hit WaitAsync() we are on the same thread, and our semaphore limits the concurrency to only allow one thread to execute that block of code at a time but it wont allow our same thread to enter that code either? the semaphore obviously cant be obtained by say some other threads like thread4 or thread5, but it also cant be obtained even by our same thread again?
没错。一些同步协调原语确实有能力允许递归锁(例如,Monitor
),但其他的则没有(例如,Mutex
)。但是,异步协调原语支持递归锁是不自然的。 (就个人而言,我 against recursive locking in general)。同步协调原语可以摆脱递归,因为存在 "a thread" 即 "owns" 锁的概念。对于异步协调原语,没有 thread 拥有锁的概念;相反,"block of code" 拥有锁。
所以,说 SemaphoreSlim.WaitAsync
不是递归的(也不应该是)是一种冗长的说法。
现在,这是否是一个好的用户体验设计是另一个问题。正如评论中所指出的,如果您只想让一个按钮在时间。也就是说,如果您想要允许用户排队超过一个长[=],您可以使用SemaphoreSlim
方法41=] 操作。在这种情况下,SemaphoreSlim
就像您的代码的一种隐式队列。
这个问题的上下文是一个 WPF 应用程序。 WPF 应用程序使用 DispatcherSynchronizationContext。
如果我的应用程序中有一个调用 Button_Click 处理程序方法的按钮,并且我想确保该函数中的所有代码仅由一个线程执行,我会将其包装在一个信号量中,如图所示?但我不明白这是如何工作的。
假设按钮被点击,我们会点击 WaitAsync() ,returns 一个任务在输入信号量时完成,所以我猜是立即?然后我们将点击 await GetLengthAsync() 这将使我们跳回 wpf 消息循环。假设 10 秒过去,再次点击按钮,那么我们将再次进入 Button_Click 方法并点击 WaitAsync(),这 returns 当我们进入信号量时完成的任务,我们无法进入信号量让我们跳回消息循环?是这样吗?
主要问题 - 我们两次点击 WaitAsync() 我们都在同一个线程上,我们的信号量将并发限制为一次只允许一个线程执行该代码块,但它也不允许我们的同一个线程输入该代码吗?信号量显然不能通过其他线程(如 thread4 或 thread5)获取,但即使是我们的同一个线程也无法再次获取?任何澄清将不胜感激。
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();
}
}
Assuming the button was clicked, we would hit WaitAsync() which returns a task that completes when the semaphore is entered, so I guess immediately? Then we would hit await GetLengthAsync() which would bounce us back out to the wpf message loop.
是的,是的。
Assuming 10 seconds goes by and the button is clicked again, then we would enter the Button_Click method again and hit WaitAsync(), which returns a task that completes when we enter the semaphore, and we cant enter the semaphore so we bounce back out to the message loop? is that how it works?
是的。
MAIN QUESTION - Both times we hit WaitAsync() we are on the same thread, and our semaphore limits the concurrency to only allow one thread to execute that block of code at a time but it wont allow our same thread to enter that code either? the semaphore obviously cant be obtained by say some other threads like thread4 or thread5, but it also cant be obtained even by our same thread again?
没错。一些同步协调原语确实有能力允许递归锁(例如,Monitor
),但其他的则没有(例如,Mutex
)。但是,异步协调原语支持递归锁是不自然的。 (就个人而言,我 against recursive locking in general)。同步协调原语可以摆脱递归,因为存在 "a thread" 即 "owns" 锁的概念。对于异步协调原语,没有 thread 拥有锁的概念;相反,"block of code" 拥有锁。
所以,说 SemaphoreSlim.WaitAsync
不是递归的(也不应该是)是一种冗长的说法。
现在,这是否是一个好的用户体验设计是另一个问题。正如评论中所指出的,如果您只想让一个按钮在时间。也就是说,如果您想要允许用户排队超过一个长[=],您可以使用SemaphoreSlim
方法41=] 操作。在这种情况下,SemaphoreSlim
就像您的代码的一种隐式队列。