【发布时间】:2019-03-30 09:57:19
【问题描述】:
这个问题的上下文是一个 WPF 应用程序。 WPF 应用程序使用 DispatcherSynchronizationContext。
如果我的应用程序中有一个调用 Button_Click 处理程序方法的按钮,并且我想确保该函数中的所有代码仅由一个线程执行,我会将其包装在一个信号量中,如图所示?但我不明白这是如何工作的。
假设按钮被点击,我们会点击 WaitAsync() ,它返回一个在输入信号量时完成的任务,所以我猜是马上?然后我们会点击 await 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();
}
}
【问题讨论】:
-
这不会很好地结束,重入错误很讨厌。您需要将按钮的 IsEnabled 属性设置为 false,而不是信号量。
-
@HansPassant 这不是异步代码中的常见问题或所需功能吗?还是表示设计错误?
-
重新进入从来都不是一个理想的能力。您的代码要么是为并发而设计的,要么不是。重入是错误的标志。我第二个@HansPassant,如果你在等待 GetLengthAsync 之前禁用按钮,你会更好。
-
@Nick 我明白了,但它是否与按钮绑定并不是我最关心的问题。我只是想了解一下程序的执行流程,你熟悉它的工作原理吗?
-
流程清晰。如果您在 LongComputeFunc 运行时单击该按钮,您的代码将从线程池中获取一个线程来阻塞信号量并等待它发出信号,然后再继续执行该方法。但是,如果您单击太多次,您可能会耗尽线程池,即导致其中的所有线程在信号量上等待,然后单击只会排队等待线程池。当 LongComputeFunc 完成时,如果同步上下文在主线程上运行 Release,你会很幸运。否则你可能会死锁。
标签: c# multithreading asynchronous semaphore