我正在尝试了解信号量的。
简而言之,我在InitializeNamesAsync("","","")
中放置了一个{long}运行过程(用于访问网络资源)KeyUp event handler
。我试图在viewNames
初始化InitializeNamesAsync()
的同时允许用户连续输入而不会降低速度。由于用户将不断输入内容,因此KeyUp event handler
方法运行时,InitializeNamesAsync()
将被调用多次。
虽然以下代码可以正常编译,但它会永远锁定,从而完全停止键盘输入。
所以我的问题是:
TIA
已定义
ResourceLock = new Semaphore(0, 1);
private async void _cboLastName_KeyUpAsync(object sender, KeyEventArgs e)
{
if (viewNames == null)
{
ResourceLock.WaitOne();
await InitializeNamesAsync("", "", "");
ResourceLock.Release();
}
}
答案 0 :(得分:2)
尽管您正在使用Semaphore
来允许多个线程进入并执行关键区域内的事件,但是您的设计仍存在基本问题,但挑战在于,您正在阻止哪个线程?
由于事件是在Ui thread
上执行的,该事件只有1个并且是唯一的,所以发生的是:
WaitOne
,并已完成对您的阻止,它甚至没有按预期执行Async方法。检查以下控制台代码,您认为结果如何?
以下代码导致死锁,因为Ui或主控制台线程正在等待自身
async Task Main()
{
Semaphore s = new Semaphore(0, 2);
for(int x = 0; x < 5;x++)
{
s.WaitOne();
await Test(x);
s.Release();
}
}
async Task Test(int x)
{
$"Entering : {x}".Dump();
await Task.Delay(3000);
}
await Test(x);
和s.Release();
从未被调用有哪些选项,请查看修改后的设计:
async Task Main()
{
for(int x = 0; x < 5;x++)
{
await Test(x);
s.WaitOne();
}
}
Semaphore s = new Semaphore(0,2);
async Task Test(int x)
{
$"Entering : {x}".Dump();
await Task.Delay(3000);
s.Release();
}
这里有什么不同:
WaitOne
之前已调用异步方法Release
发生在异步方法完成后,而不是在同一线程(在这种情况下,在线程池线程上)您会发现此代码将成功执行且没有任何死锁
有什么解决方案:
WaitOne
,这是导致死锁的秘诀,尤其是当Release
也被安排在同一线程上时Release
(我使用了Async方法,在这种情况下使用了Threadpool线程)其他详细信息:
ManualResetEvent
,它们支持AutoResetEvent
用例答案 1 :(得分:1)
该线程被阻止是因为您输入了两次并且信号量不允许两次输入相同的线程(例如Monitor.Enter允许-但不清楚为什么在这里需要它)。
据我了解,您需要在后台启动初始化。
由于它是UI线程,因此您可能不需要使用同步原语(在这种情况下,至少通常不需要这样做)。我认为只要有两个像这样的变量就足够了
beingInitialized
并使用类似的代码进行初始化
private async void EnsureInitialized()
{
if(!initialized && !beingInitialized)
{
beingInitalized = true;
await StartInitialization();
initalized = true;
beingInitialized = false;
}
}
然后称它为火而忘了
喜欢
private async void _cboLastName_KeyUpAsync(object sender, KeyEventArgs e)
{
EnsureInitialized();
...