-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
}

The above code works perfect IF you comment out the two grdMain.Dispatcher.Invoke(...);. It also works perfekt if you comment out the *.Set(); and *.WaitOne(); But WHYYYY? I need both ^^. I don't get it...

CodingYourLife
  • 7,172
  • 5
  • 55
  • 69

2 Answers2

3

Assuming WaitForDriveToBecomeReady is called on the Dispatcher's thread, you are explicitly introducing a deadlock.

Consider the course of execution

  • You set up the reset event
  • The thread starts executing
  • The dispatcher thread calls syncEvent.WaitOne(), that thread is now blocked until that event gets set
  • The second thread executes the Dispatcher.Invoke; this puts a message in the Dispatcher's queue and waits until it processes it (on the main thread)

So you have the main thread blocked waiting for the event that will eventually be set by the second thread. And you have the second thread blocked waiting for the main thread to process a message. Textbook deadlock.

In general, waiting on the UI thread is bad; it's easy to get deadlocks like this, but even if it works, you're still blocking the UI from updating, creating an unresponsive program. Based on the snippet above it's difficult to say how best to reorganize your code so that you don't have to block the UI thread, but it seems like this concept (preparing a drive, and doing something once it's ready) would be a candidate for an asynchronous method.

Jacob
  • 1,699
  • 10
  • 11
  • I guess you got the point right. The problem occurs only with this dispatcher.invoke line so I think because using the main thread again this waiting in main thread becomes incompatible, right? Sorry but that took me hours and made me crazy ^^. – CodingYourLife Apr 05 '13 at 02:23
  • A way to think about it is that Dispatcher.Invoke is very much like your event + thread code (both in concept and implementation). It queues work on another thread, and then waits for that thread to set an event when the work is done. So you have each thread doing nothing, waiting for the other to finish its work. – Jacob Apr 05 '13 at 02:48
  • Thank you very much. I now had time to go through a few articles and finally made it! I'm sorry that I accepted my own answer instead of mine, but I wanted to share the source code for my problem. – CodingYourLife Jun 25 '13 at 15:38
0

I finally had time to continue read more about async and await. Thanks @Jacob for pointing out what was the problem.

Here is my working async code for a toast notification that appears as long as you don't connect your drive.

//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.");
}
CodingYourLife
  • 7,172
  • 5
  • 55
  • 69