12

I am developing a C# Winforms application, part of the application will be uploading files to a webserver using AsyncUpload (using it,due to the need to use a porgress callback) , In the C# program

i got a simple for loop that calls The Uploading function

 for(int i=0;i < 10 ; i++)
{
  Uploadfun();
}

And the fun does some magic:

Uploadfun()
  { 
  // Logic comes here

   // webClient.UploadFileAsync runs a 2nd thread to perform upload .. 
   webClient.UploadFileAsync(uri, "PUT", fileNameOnHD);  

 }

And a callback that gets called when the Async upload is done

Upload_Completed_callback()
{
  //Callback event
}

Edit

The logic sequence:

  1. Fun gets called (from loop)
  2. Fun logic is executed and done..
  3. Goes back to for loop
  4. Callback will be called eventually, when UploadFileAsync (which is running some logic in another thread) will end

The problem is on the 3rd point, when the execution moves back to the for loop, i need to block the loop from continuing until the callback get called.

Madi D.
  • 1,980
  • 5
  • 23
  • 44
  • 3
    Do you have access to `fun` implementation? You should probably consider providing a synchronous interface on which the async API is implemented. – Mehrdad Afshari Feb 06 '10 at 16:35
  • The way you are describing it, your code seems sequential and single threaded, which means that until the `callback` function is called the loop won't continue. Please correct me if I am wrong by giving more detailed explanation. – Darin Dimitrov Feb 06 '10 at 16:36
  • @Madi: Then you're doing this backwards. You should use the synchronous version of `UploadFile` at the core of the logic and use an async API on top of it if you need it. – Mehrdad Afshari Feb 06 '10 at 16:42
  • 1
    @Mehrdad: i am actually forced to use the Async version, because it provides me with "progress" callbacks, which is a requirement. – Madi D. Feb 06 '10 at 16:45

4 Answers4

20

So if I understand correctly, you want to call UploadFileAsync then block until the async call has hit your callback. If so, I'd use AutoResetEvent i.e

private readonly AutoResetEvent _signal = new AutoResetEvent(false); 

fun()
  { 
  // Logic comes here

   // runs a 2nd thread to perform upload .. calling "callback()" when done
   webClient.UploadFileAsync(uri, "PUT", fileNameOnHD);  

   _signal.WaitOne();   // wait for the async call to complete and hit the callback     
 }



callback()
 {
   //Callback event
   _signal.Set(); // signal that the async upload completed
 }

Using AutoResetEvent means that the state gets automatically reset after Set has been called and a waiting thread receives the signal via WaitOne

zebrabox
  • 5,694
  • 1
  • 28
  • 32
  • when using the Waitone() after the uploadAsync , The program is halting, and the Callback is not being called. – Madi D. Feb 06 '10 at 17:14
  • okie.. i fixed the halting problem,called fun() in a separate thread, "obviously" calling it in the main thread is causing the issue !, @Juliet Kinda helped point out the problem.. thanks both of you =) – Madi D. Feb 07 '10 at 14:10
4

In C# methods block by default, so you shouldn't need to do anything. I'm assuming that for some reason you are calling a non-blocking method that starts a background task / thread / whatever and gives you a callback when it's done. You want to call this asynchonous method in a synchronous manner.

You can call fun from inside the callback. Something along these lines (pseudo-code):

int n;

callFunTenTimes()
{
    n = 0;
    fun(n);
}

callback()
{
    ++n;
    if (n < 10)
       fun(n);
    else
       print("done");
}

This is similar to continuation passing style.

An advantage to this method is that you can also make your method asynchronous without adding any extra threads, locks, or extra logic - you just supply a callback function which your client can subscribe to. It works well in an event-driven environment.

Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
  • Thats funny. Ive been thinking about my own problem along these lines: having the callback function do the 'meat' of what the loop used to do. This solution seems to keep the wait time to a minimum. – Joe Feb 06 '10 at 17:06
  • i need the async version because it provides a "progress" callback which is a core requirement. – Madi D. Feb 06 '10 at 17:12
  • @Madi D: If the requirement is that the upload must behave asynchronously, why not make your function an async function too, with a callback? Can you explain more about what you are using this for? Is this a WinForms app, ASP.NET, etc? – Mark Byers Feb 06 '10 at 18:48
  • i edited the question and added answers to you questions, plus some extra info. – Madi D. Feb 06 '10 at 22:39
  • +1 for teaching me a new concept, and giving me an alternative solution! – Madi D. Feb 07 '10 at 14:10
3

Zebrabox does have it right using a WaitHandle. While Juliet's solution does work, the thread performing the spin-wait will consume a significant amount of processor in proportion to the WaitHandle, which would essentially be sitting idle.

Grant Back
  • 542
  • 2
  • 8
  • It won't consume very much processor, but it may prevent the system from putting the CPU into a deeper idle state and hence draw down the battery faster on a laptop. – Ben Voigt Feb 08 '10 at 18:59
1

The problem is here:

for(int i=0;i < 10 ; i++)
{
  fun(); <-- if we block until this function finishes here, we stop the UI thread
}

What you're doing is sequential. And if you can't afford to block the UI thread, move the loop off the UI thread:

volatile downloadComplete;

void DownloadUpdates()
{
    ThreadPool.QueueUserWorkItem(state =>
        for(int i = 0; i < 10; i++)
        {
            downloadComplete = false;
            webClient.UploadFileAsync(uri, "PUT", fileNameOnHD);
            while(!downloadComplete) { Thread.Sleep(1); }
        });
}

Upload_Completed_callback()
{
    downloadComplete = true;
}

Now you can block the execution of the loop without halting your UI thread, and you also get the benefit of progress indicators from the webclient class.

Juliet
  • 80,494
  • 45
  • 196
  • 228
  • Don't use a boolean variable for this, the compiler can optimize it away (which would cause the behavior of never continuing that the OP mentions). zebrabox showed the right way to do it, with a waitable event object. – Ben Voigt Feb 06 '10 at 17:47
  • 1
    @Ben: I appreciate your comments, but I think they're misleading. First, the compiler isn't going to optimize the bool away as long as its used or assigned somewhere (and presumably it is). Second, spin-waiting on a bool and resetEvents are valid ways to block a thread, both are the "right way to do it", neither are "wrong", choosing one or the other depends on the tastes of the programmer. – Juliet Feb 06 '10 at 19:52
  • Thanks alot for the really useful answer,endded up combining the threading from your solution with @zebrabox's resetEvent solution, and now it is working.. =) – Madi D. Feb 07 '10 at 14:18
  • @Juliet, According to the rules of the .NET memory model, these two are exactly equivalent: while(!downloadComplete) { Thread.Sleep(1); } and if (!downloadComplete) { while (true) { Thread.Sleep(1); } } Since the loop doesn't change the variable, the compiler can move the test outside the loop. I see that now you've added a syntax error trying to make downloadComplete volatile. If you fix the declaration, it will prevent the compiler from doing the optimization I mentioned, but it is still polling which is worse than the event for a number of reasons. – Ben Voigt Feb 08 '10 at 18:58
  • @Ben: I appreciate your comment, but `while(!downloadComplete) { Thread.Sleep(1); }` and `if (!downloadComplete) { while (true) { Thread.Sleep(1); } }` are *not* equivalent in any memory model. Would you mind providing some MSDN documentation or a code sample demonstrating how polling compiles into the code you've provided (with or without volatile)? – Juliet Feb 09 '10 at 00:33
  • @Juliet: Here's a very informative discussion in the context of Java, but this particular case is identical in C# (because the optimization is legal under sequential consistency and both Java and .NET CLR memory models are weaker than that): http://java.sun.com/docs/books/jls/third_edition/html/memory.html Look especially at section 17.9 of that page. – Ben Voigt Feb 11 '10 at 03:56