C# AutoResetEvent WaitOne stopped by dispatcher
本文关键字:by dispatcher stopped WaitOne AutoResetEvent | 更新日期: 2023-09-27 18:07:46
private void WaitForDriveToBecomeReady()
{
AutoResetEvent syncEvent = new AutoResetEvent(false); //set wait signal to use later
//dispatcher to be able to change stuff in xaml from within thread
Action action1 = new Action(delegate() { grdMain.Children.Add(notification); });
Action action2 = new Action(delegate() { grdMain.Children.Remove(notification); });
Thread restoreThread1 = new Thread(()=>{
grdMain.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background, action1); //show a notification
Thread.Sleep(1500); //sleep a bit...
grdMain.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background, action2); //hide a notification
syncEvent.Set(); //signal to continue at *.WaitOne()
});
restoreThread1.Start();
syncEvent.WaitOne(); //let main thread wait until *.Set(); is called
}
如果你注释掉这两个grdMain.Dispatcher.Invoke(…);如果注释掉*.Set(),它也可以完美地工作;和* .WaitOne ();但WHYYYY呢?我两个都需要^^。我不明白…
假设在Dispatcher的线程上调用WaitForDriveToBecomeReady
,则显式地引入了死锁。
考虑执行过程
- 你设置了重置事件
- 线程开始执行
- 调度线程调用
syncEvent.WaitOne()
,该线程现在被阻塞,直到该事件被设置 - 第二个线程执行
Dispatcher.Invoke
;这将一个消息放入Dispatcher的队列中,并等待Dispatcher处理它(在主线程上)
因此主线程阻塞等待事件,该事件最终将由第二个线程设置。第二个线程被阻塞,等待主线程处理消息。教科书僵局。
一般来说,等待UI线程是不好的;这样很容易产生死锁,但即使它有效,您仍然会阻止UI更新,从而创建一个无响应的程序。根据上面的代码片段,很难说如何最好地重新组织代码,以便不必阻塞UI线程,但似乎这个概念(准备驱动器,并在准备好后执行某些操作)将是异步方法的候选。
我终于有时间继续阅读有关async和await的更多内容。谢谢@Jacob指出问题所在。
这是我的工作异步代码toast通知出现只要你不连接你的驱动器。
//the method
public async Task WaitForDriveAsync(string path, string waitingToastText)
{
int width = 300;
int height = 125;
TextBlock toastTextBlock = new TextBlock() { Text = waitingToastText, HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center, FontSize = 23, Width = (width - 15), TextWrapping = TextWrapping.WrapWithOverflow };
Grid notification = new Grid();
notification.Width = width;
notification.Height = height;
notification.Background = Brushes.Red;
notification.Margin = new System.Windows.Thickness(0, 27, 0.4, 0);
notification.VerticalAlignment = VerticalAlignment.Top;
notification.HorizontalAlignment = HorizontalAlignment.Right;
notification.Children.Add(toastTextBlock);
grdMain.Children.Add(notification);
while (!Directory.Exists(path))
{
await Task.Delay(1000);
}
grdMain.Children.Remove(notification);
}
//to call it
private async void btnBackupNow_Click(object sender, RoutedEventArgs e)
{
await WaitForDriveAsync(@"B:'", "Please connect your drive.");
}