5

I'm a little confused over a ManualResetEvent that I'm using which doesn't appear to be unblocking. Anyone know why this might be the case?

The scenario I've got is something along these lines. The real situation is quite complicated and I've not managed to isolate a section of code that's reasonable to post to reproduce the issue.

EDIT
I've updated the code example below. This is execute in a number of different dialogs and I have noticed that one of them hits the this.mre.WaitOne(); Then what happens is I get a "Server Busy" dialog, where I need to press 'switch to' or 'retry', which will then allow my code to step passed the WaitOne() call and all will work. I'm not sure how its relevant, but obviously its of some important.

public class A
{
 ManualResetEvent mre;

 public void Start(ThreadClass tc)
 {
    this.mre = new ManualResetEvent(false);
    tc.Begin();

    WebClient wc = new WebClient();
    // progress events are pumped to the ThreadClass which then update the Form2.
    wc.DownloadFileCompleted += new System.ComponentModel.AsyncCompletedEventHandler(wc_DownloadFileCompleted);

    wc.DownloadFileAsync("Src", "Tgt");
    this.mre.WaitOne();
 }

 void void wc_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
 {
    try
    {
     // Do Stuff
    }
    finally
    {
      this.mre.Set();
    }
 }
}

public class ThreadClass
{
   Begin()
   {
      Thread t = new Thread(new ThreadStart(DoWork));
      t.Start();
   }

   private void DoWork()
   {
     Form f = new Form2();
     f.ShowDialog();

     // Sits waiting on another ResetEvent to determine when to close the thread.
   }
}
Ian
  • 33,605
  • 26
  • 118
  • 198
  • Is your `wc_DownloadFileCompeted` method being called? – Jon Skeet Jul 30 '09 at 10:14
  • Jon, Yes it is. I'm getting right through to the Set() which is executing fine, but my main thread still blocks. The example has been simplified but if I call Set() elsewhere (for example I've got a dialog that appears, with a cancel button which also has an event. Then the Set() method in my lambda attached to this event works fine). – Ian Jul 30 '09 at 10:18
  • divo, the Set() will most likely be called by a separate thread, as I'm assuming the WebClient must create a new one to actually download the file. It's the thread that the WebClient uses to send it's event that will be calling the Set(). – Ian Jul 30 '09 at 10:27
  • 3
    @Ian: Your sample works without problems when I use the code in a .NET 3.5 command line application. The problem might lie within the way your code is called or something else that happens in your code. – Dirk Vollmar Jul 30 '09 at 10:29
  • @divo, works on my machine too. – bruno conde Jul 30 '09 at 10:34
  • Is the problem reproducible with this exact piece of code? – Naveen Jul 30 '09 at 10:43
  • I'm thinking there must be something else going on underneath, as there are a few other threads involved in kicking this whole process off. As yet I've not managed to isolate enough to post another example. – Ian Jul 30 '09 at 10:46

4 Answers4

4

Webclient runs in the same thread as your caller, so that thread is blocked at the WaitOne, it doesn't actually create a new thread for you.

Move your code into a BackgroundWorker or simply, don't block but wait for the DownloadComplete event to be raised.

Ray Hayes
  • 14,896
  • 8
  • 53
  • 78
  • 1
    Ray, I'm not sure that I follow you? My thread that calls DownloadFileAsync() should be blocking until the file has downloaded. – Ian Jul 30 '09 at 10:28
  • Your code is all running in a single thread (try putting debug traces with the thread ID to confirm), that means you are trying to "Wait" and execute the WebClient download at the same time. The internal scheduling may do what you want, or you may start getting errors like you're getting. If you need to wait, either do not call the Async version of the method (that would then be an implicit wait) or make it a true multithreaded application. – Ray Hayes Jul 30 '09 at 14:03
  • I've updated my example slightly. I'm not exactly sure the UI events get processed. I expect that they'll be on the newly created thread when FormB is displayed. Honestly I'm not sure how that relates to the ResetEvent not being trigger though? Or am I missing something? – Ian Jul 30 '09 at 16:09
3

Check that the MRE you're setting is actually the same MRE you're waiting on. You say this is a simplified example - is it possible that in the real code you're creating two different reset events? That would fairly obviously break things :)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Jon, the MRE's are the same. Checked that one earlier. Just trying to see if I can simplify the actual code because its quite involved. – Ian Jul 30 '09 at 10:41
  • Have tried to put an update to the code structure. Unfortuanetly there are 3/4 classes involved using all sorts of events and delegates which I think are pretty trivial but are required for a much fuller example. – Ian Jul 30 '09 at 16:11
2

I have modified your code a bit and it will work as supposed to now. The problem was that you should have passed the MRE object as the user state parameter of the DownloadFileAsync method:

public class A 
{  
 public void Start(ThreadClass tc) 
 { 
    ManualResetEvent mre = new ManualResetEvent(false);
    WebClient wc = new WebClient(); 
    // progress events are pumped to the ThreadClass which then update the Form2. 
    wc.DownloadFileCompleted += new System.ComponentModel.AsyncCompletedEventHandler(wc_DownloadFileCompleted); 

    wc.DownloadFileAsync("Src", "Tgt", mre); 
    mre.WaitOne();
    mre.Close();
 } 

 void void wc_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e) 
 { 
    try 
    { 
     // Do Stuff 
    } 
    finally 
    { 
      (e.UserState as ManualResetEvent).Set();
    } 
 } 
} 
1

Why not use wc.DownloadFile instead of wc.DownloadFileAsync if you want it to block anyways..

Yvo
  • 18,681
  • 11
  • 71
  • 90
  • Because the WebClient events are only available when downloading asynchronously, and I wish to display download information back to the user, yet block any further progress of my app until I can be sure that the file is available to open. – Ian Jul 30 '09 at 10:25
  • @Ian, but you are also blocking your GUI too... you know the file is available in the DownloadComplete event, so move your post-WaitOne code to inside the event handler! – Ray Hayes Jul 30 '09 at 14:05