问题描述
此问题的上下文是WPF应用程序.WPF应用程序使用DispatcherSynchronizationContext.
The context for this question is a WPF application. WPF applications use a DispatcherSynchronizationContext.
如果我的应用程序中有一个调用Button_Click处理程序方法的按钮,并且我想确保该函数中的所有代码仅由一个线程执行,我会将其包装为一个信号量,如图所示?但是我不明白这是如何工作的.
If I have a button in my application that invokes the Button_Click handler method and I want to ensure all the code in that function is only ever executed by one thread I would wrap it in a semaphore as shown? But what I dont understand is how this works.
假设单击了按钮,我们将点击WaitAsync(),它返回一个在输入信号量时完成的任务,所以我马上猜出来吗?然后,我们将等待aa GetLengthAsync(),它将使我们跳回wpf消息循环.假设经过了10秒钟,再次单击了按钮,那么我们将再次进入Button_Click方法并单击WaitAsync(),这将返回一个在输入信号量时完成的任务,并且我们无法输入信号量,因此我们会跳回消息循环?是这样吗?
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?
主要问题-两次单击WaitAsync()时,我们都在同一线程上,并且信号量将并发性限制为一次仅允许一个线程执行该代码块,但也不允许我们的同一线程输入该代码?信号量显然无法通过说其他一些线程(例如线程4或线程5)来获得,但是即使再次使用我们的同一线程也无法获得?任何澄清将不胜感激.
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? Any Clarification would be greatly appreciated.
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();
}
}
推荐答案
是的,是的.
是的
那是正确的.某些同步协调原语确实具有允许递归锁定的功能(例如, Monitor
),但其他则没有(例如, Mutex
).但是,异步协调原语支持递归锁是不自然的.(就我个人而言,我是总体上反对递归锁定).同步协调原语可以避免递归,因为存在拥有"锁的线程"概念.对于异步协调原语,不存在拥有锁的 thread 的概念.而是由代码块"拥有锁.
That is correct. Some synchronous coordination primitives do have the capability to allow recursive locks (e.g., Monitor
), but others do not (e.g., Mutex
). However, it is unnatural for asynchronous coordination primitives to support recursive locks. (Personally, I'm against recursive locking in general). Synchronous coordination primitives can get away with recursion because there is the notion of "a thread" that "owns" the lock. For asynchronous coordination primitives, there is no notion of a thread owning the lock; rather, a "block of code" owns the lock.
因此,这是一种冗长的说法,即 SemaphoreSlim.WaitAsync
不是递归的(也不应该是递归的).
So, that's a long-winded way of saying that SemaphoreSlim.WaitAsync
is not recursive (and shouldn't be).
现在,这是否是一个好的UX设计是另一个问题.如评论中所述,如果您一次只想启动一个按钮,则UI disable 代表长时间运行的按钮更为常见.也就是说,如果您想允许允许用户排队多个长时间运行的操作,则可以可以使用 SemaphoreSlim
方法.在这种情况下, SemaphoreSlim
的作用就像是一种针对您的代码的隐式队列.
Now, whether this is a good UX design or not is a different question. As noted in the comments, it's more common for UI to disable a button that represents a long-running operation if you only want one to be started at a time. That said, you could use the SemaphoreSlim
approach if you want to allow the user to queue up more than one long-running operation. In this case, the SemaphoreSlim
acts like a sort of implicit queue for your code.
这篇关于SemaphoreSlim.WaitAsync如何允许异步函数的顺序执行?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!