-1
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(); 但是为什么?我两个都需要^^。我不明白...

4

2 回答 2

3

假设WaitForDriveToBecomeReady在 Dispatcher 的线程上被调用,您将显式引入死锁。

考虑执行过程

  • 您设置了重置事件
  • 线程开始执行
  • 调度程序线程调用syncEvent.WaitOne(),该线程现在被阻塞,直到该事件被设置
  • 第二个线程执行Dispatcher.Invoke;这会将消息放入 Dispatcher 的队列并等待它处理它(在主线程上)

所以你让主线程阻塞等待最终将由第二个线程设置的事件。并且您让第二个线程阻塞等待主线程处理消息。教科书僵局。

一般来说,在 UI 线程上等待是不好的;像这样很容易出现死锁,但即使它有效,您仍然会阻止 UI 更新,从而创建一个无响应的程序。根据上面的代码段,很难说如何最好地重新组织您的代码,以便您不必阻塞 UI 线程,但似乎这个概念(准备驱动器,并在它准备好后做某事)将是一个候选者对于异步方法

于 2013-04-05T01:54:40.900 回答
0

我终于有时间继续阅读有关 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.");
}
于 2013-06-25T15:42:21.057 回答