4

'New' meaning from a thread pool.

Given the example below, my assumptions are:

  1. Main method executes on one thread (eg. thread 1)
  2. BeginInvoke uses available thread in pool to execute AsyncDemo.TestMethod (thread 2)
  3. An async method call, eg WebClient.UploadStringAsync uses another available thread (thread 3)

Number three is where my question stems from, The definition of WebClient.UploadStringAsync: Uploads the specified string to the specified resource. These methods do not block the calling thread.

Does this mean it uses another available thread in the pool? Is this an ill-advised technique (async within an async)?

My ultimate goal is to post some data asynchronously (fire and forget), and was using UploadStringAsync previously. Now I have decided to encapsulate the surrounding code with BeginInvoke as well, but thinking if I have to change the UploadStringAsync to a synchronous method instead (UploadString()).

Thanks for any help!

public class AsyncMain 
{
    // The delegate will be called asynchronously.
    public delegate string AsyncMethodCaller(out int threadId);

    public static void Main() 
    {       
        // The asynchronous method puts the thread id here.
        int threadId;       

        //Create the delegate.
        AsyncMethodCaller caller = new AsyncMethodCaller(AsyncDemo.TestMethod);

        // Initiate the asychronous call.
        IAsyncResult result = caller.BeginInvoke(out threadId, null, null);     

        Console.WriteLine("In AsyncMain.Main() Thread {0} does some work.", Thread.CurrentThread.ManagedThreadId);

        // Call EndInvoke to wait for the asynchronous call to complete,
        // and to retrieve the results.
        string returnValue = caller.EndInvoke(out threadId, result);

        Console.WriteLine("The async call executed on thread {0}, has responded with \"{1}\". The result is {2}", threadId, returnValue, result);
    }
}


public class AsyncDemo 
{   
    // The method to be executed asynchronously.
    public static string TestMethod(out int threadId) 
    {
        //get threadId, assign it.
        threadId = Thread.CurrentThread.ManagedThreadId;
        Console.WriteLine("TestMethod() begins at thread {0}", threadId);

        //Do work

        //Do ASYNC Method such as: WebClient.UploadStringAsync

        return String.Format("I'm finished my work.");
    }
}
  • 1
    Is there any reason you aren't using TPL methods? – Selali Adobor Aug 08 '14 at 22:35
  • Sorry, I neglected to mention I'm on .NET 3.5 (on MSDN notes it's only available for 4 and 4.5). Forunately, I've seen something on nuget about TPL for .NET 3.5, but unfortunately, I'm not in the position to introduce new packages to the project at this time. – Brandosaurus Aug 08 '14 at 22:45
  • For others, you can import the TPL package for 3.5 with the package manager command `Install-Package TaskParallelLibrary. You can even wrap APM methods (Begin/End) in Tasks. – Selali Adobor Aug 08 '14 at 23:01

1 Answers1

3

Does this mean it uses another available thread in the pool?

Short answer: yes.

  1. According to the docs for UploadStringAsync:

    The string is sent asynchronously using thread resources that are automatically allocated from the thread pool.

  2. And BeginInvoke uses a thread from the thread pool to accomplish its asynchronous behavior.

Is this an ill-advised technique (async within an async)?

If you need asynchronous behavior at both levels, then you do what you gotta do. In general, though, the best advice is to write the simplest code you can that will work, so if you can get away with just one level of indirection, I'd say go for it.

It's unfortunate that you can't upgrade to a newer version of .NET, because with newer versions you can accomplish the same thing much more simply using Tasks:

public static void Main() 
{       
    Task<string> task = AsyncDemo.TestMethod();

    Console.WriteLine("In AsyncMain.Main() Thread {0} does some work.", Thread.CurrentThread.ManagedThreadId);

    string returnValue = task.Result; // This will cause the main thread to block until the result returns.

    Console.WriteLine("The async call executed on thread {0}, has responded with \"{1}\". The result is {2}", threadId, returnValue, result);
}



public class AsyncDemo 
{   
    // The method to be executed asynchronously.
    public async static Task<string> TestMethod() 
    {
        Console.WriteLine("TestMethod() begins");

        //Do work

        await new WebClient().UploadStringTaskAsync("...", "...");

        return String.Format("I'm finished my work.");
    }
}
Community
  • 1
  • 1
StriplingWarrior
  • 151,543
  • 27
  • 246
  • 315
  • It should also be noted that wrapping UploadStringAsync in BeginInvoke/EndInvoke calls won't make the behavior "blocking". UploadStringAsync will execute on it's on thread without any relation or synchronization to the BeginInvoke thread. But yes, TPL or async/await would be much better. – Marcel N. Aug 08 '14 at 22:52
  • Yes, I would love to upgrade .NET, but we're maintaining a legacy project with a zillion moving parts that I do not want to touch :). But thank you for the response! – Brandosaurus Aug 08 '14 at 23:15