KeyUp 事件处理程序中的信号量——锁定键盘输入
Semaphore in KeyUp event handler -- locks keyboard input
我正在尝试了解 Semaphore 的。
简而言之,我在 KeyUp event handler
中放置了 "long" 运行 过程(访问网络资源)InitializeNamesAsync("","","")
。当 viewNames
被 InitializeNamesAsync()
初始化时,我试图让用户在不减速的情况下连续打字。由于用户将连续键入,因此 KeyUp event handler
将被调用多次,而 InitializeNamesAsync()
方法是 运行.
虽然下面的代码可以正常编译,但它会永远锁定,完全停止键盘输入。
所以我的问题是:
- 这是对 Semaphore 的适当使用吗?
- 我怎样才能完成这项工作?
- 有没有更好的方法?
TIA
已定义
ResourceLock = new Semaphore(0, 1);
private async void _cboLastName_KeyUpAsync(object sender, KeyEventArgs e)
{
if (viewNames == null)
{
ResourceLock.WaitOne();
await InitializeNamesAsync("", "", "");
ResourceLock.Release();
}
}
你的设计存在根本问题,虽然你使用 Semaphore
允许多个线程进入并执行临界区内的事件,但挑战是,你是哪个线程阻塞?
由于事件是在 Ui thread
上执行的,它只有 1 并且是唯一的,所以发生的事情是:
- 您的代码进入事件,在 Ui 线程上为信号量调用
WaitOne
并且它完成了您被阻止,它甚至没有按预期执行异步方法
Check the out the following Console code, what do you think is the result ?
以下代码导致死锁,因为 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();
从未被调用
What are the options, review modified design:
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();
}
What's different here:
- 在调用信号量
WaitOne
之前调用了异步方法
- 信号量
Release
发生 post 异步方法的完成,而不是在同一线程上(在本例中是在线程池线程上)
你会发现这段代码会成功执行,没有任何死锁
What's the solution:
- 不要在像 Ui 线程这样的唯一线程上调用
WaitOne
,这会导致死锁,尤其是当 Release
也被安排在同一线程上时
- 在单独的线程上调用
Release
(我使用了Async方法,在这种情况下使用Threadpool线程)
Other Details:
- 理想情况下,Semaphore 是为多个线程进入临界区而设计的,如果您只期望一个线程,那么 Sempahore 可能不是正确的选择,但它有助于向线程发送信号,这与锁不同,您也可以查看
ManualResetEvent
和 AutoResetEvent
,支持 Signaling / EventWaitHandle
个用例
线程被阻塞,因为你输入了两次,而信号量不允许输入同一个线程两次(而例如 Monitor.Enter 允许——但不清楚为什么你在这里需要它)。
据我所知,您需要在后台启动初始化。
由于它是 UI 线程,您可能不需要使用同步原语(至少在这种情况下,通常不需要)。我认为只要有两个变量就足够了
正在初始化
并使用类似
的代码进行初始化
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();
...
我正在尝试了解 Semaphore 的。
简而言之,我在 KeyUp event handler
中放置了 "long" 运行 过程(访问网络资源)InitializeNamesAsync("","","")
。当 viewNames
被 InitializeNamesAsync()
初始化时,我试图让用户在不减速的情况下连续打字。由于用户将连续键入,因此 KeyUp event handler
将被调用多次,而 InitializeNamesAsync()
方法是 运行.
虽然下面的代码可以正常编译,但它会永远锁定,完全停止键盘输入。
所以我的问题是:
- 这是对 Semaphore 的适当使用吗?
- 我怎样才能完成这项工作?
- 有没有更好的方法?
TIA
已定义
ResourceLock = new Semaphore(0, 1);
private async void _cboLastName_KeyUpAsync(object sender, KeyEventArgs e)
{
if (viewNames == null)
{
ResourceLock.WaitOne();
await InitializeNamesAsync("", "", "");
ResourceLock.Release();
}
}
你的设计存在根本问题,虽然你使用 Semaphore
允许多个线程进入并执行临界区内的事件,但挑战是,你是哪个线程阻塞?
由于事件是在 Ui thread
上执行的,它只有 1 并且是唯一的,所以发生的事情是:
- 您的代码进入事件,在 Ui 线程上为信号量调用
WaitOne
并且它完成了您被阻止,它甚至没有按预期执行异步方法
Check the out the following Console code, what do you think is the result ?
以下代码导致死锁,因为 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();
从未被调用
What are the options, review modified design:
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();
}
What's different here:
- 在调用信号量
WaitOne
之前调用了异步方法 - 信号量
Release
发生 post 异步方法的完成,而不是在同一线程上(在本例中是在线程池线程上)
你会发现这段代码会成功执行,没有任何死锁
What's the solution:
- 不要在像 Ui 线程这样的唯一线程上调用
WaitOne
,这会导致死锁,尤其是当Release
也被安排在同一线程上时 - 在单独的线程上调用
Release
(我使用了Async方法,在这种情况下使用Threadpool线程)
Other Details:
- 理想情况下,Semaphore 是为多个线程进入临界区而设计的,如果您只期望一个线程,那么 Sempahore 可能不是正确的选择,但它有助于向线程发送信号,这与锁不同,您也可以查看
ManualResetEvent
和AutoResetEvent
,支持Signaling / EventWaitHandle
个用例
线程被阻塞,因为你输入了两次,而信号量不允许输入同一个线程两次(而例如 Monitor.Enter 允许——但不清楚为什么你在这里需要它)。
据我所知,您需要在后台启动初始化。
由于它是 UI 线程,您可能不需要使用同步原语(至少在这种情况下,通常不需要)。我认为只要有两个变量就足够了
正在初始化
并使用类似
的代码进行初始化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();
...