1

I have a WebAPI ASP.NET solution. I have set the web.config with:

<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />

In a call I need to get the results of 3 tasks (10 seconds each):

 Task<Cat> catAsync = GetCatAsync();
 Task<Dog> dogAsync = GetDogAsync();
 Task<Pig> pigAsync = GetPigAsync();

 await Task.WhenAll(catAsync , dogAsync , pigAsync );

 cat= catAsync.Result;
 dog= dogAsync.Result;
 pig= pigAsync.Result;

I can call it once, but subsequent calls to this seem to just die in a thread, Cat and Dog may run but Pig seems to vaporize. A minute or so later these start appearing:

The thread '<No Name>' (0x2f60) has exited with code 0 (0x0).
The thread '<No Name>' (0x974) has exited with code 0 (0x0).
The thread '<No Name>' (0x1ea8) has exited with code 0 (0x0).

My Tasks look like this:

    private async Task<Cat> CatAsync()
    {
        await Task.Run(() =>
            {
               //Use WCF to get some data from a service
            });

        return cat;
    }

Running IISRESET let's me run the site again once.

* EDIT

I posted the solution below that works with this example after reading the answer by Panagiotis Kanavos

Ian Vink
  • 66,960
  • 104
  • 341
  • 555
  • I have edited your title. Please see, "[Should questions include “tags” in their titles?](http://meta.stackexchange.com/questions/19190/)", where the consensus is "no, they should not". – John Saunders Jun 19 '13 at 23:08
  • I believe the “The thread X has exited with code 0 (0x0).” messages are not errors. So, what exactly happens? Do you get an exception? If the code stops executing, where does it stop? – svick Jun 20 '13 at 00:37
  • 1
    What does the calling code look like? Do you use `Wait` or `Result` when you call your method? – Stephen Cleary Jun 20 '13 at 03:31
  • +1 @StephenCleary and why use Task.Run instead of using the asynchronous WCF methods? I suspect the actual CatAsync or PigAsync code is where the problem is – Panagiotis Kanavos Jun 20 '13 at 09:56

5 Answers5

4

The problem as suggested by others is the Synchronization context. Await resumes execution in the Synchronization context (essentially the thread) that started awaiting. If that thread is already blocked, eg. by awaiting itself on something else, the code deadlocks.

To avoid this, you should follow any call to await with a call to ConfigureAwait(false) to make the runtime continue in a different thread.

The following code will run smoothly. Removing any of the ConfigureAwait calls though will cause a deadlock.

Additionally, you don't need to make WCF calls inside Task.Run, as long as you use the xxxAsync methods. This makes the code a lot cleaner as well

Finally, you should use Task.WaitAll() instead of await Task.WhenAll() if you just want to wait for completion. Task.WaitAll() doesn't mess with the syncrhonization context so you don't need to add yet another ConfigureAwait()

protected void Page_Load(object sender, EventArgs e)
    {
        var results = GetThem().Result;

    }

    private async Task<Tuple<Cat,Dog,Pig>> GetThem()
    {
        Task<Cat> catAsync = GetCatAsync();
        Task<Dog> dogAsync = GetDogAsync();
        Task<Pig> pigAsync = GetPigAsync();

        await Task.WhenAll(catAsync, dogAsync, pigAsync).ConfigureAwait(false);
        //better yet,
        //   Task.WaitAll(catAsync, dogAsync, pigAsync);

        var cat = catAsync.Result;
        var dog = dogAsync.Result;
        var pig = pigAsync.Result;
        return Tuple.Create(cat, dog, pig);
    }

    private async Task<Pig> GetPigAsync()
    {
        var cat = new Pig();
        var res = await GetGoogleSearchHTML("cat").ConfigureAwait(false);
        using (StreamReader sr = new StreamReader(res.GetResponseStream()))
        {
            cat.Name = await sr.ReadToEndAsync().ConfigureAwait(false);
        }

        return cat;
    }

    public async Task<WebResponse> GetGoogleSearchHTML(string type)
    {
        System.Net.WebRequest request = System.Net.HttpWebRequest.Create(String.Format("http://www.google.com/search?noj=1&site=cat&source=hp&q={0}&oq=search", type));
        System.Net.WebResponse response = await request.GetResponseAsync().ConfigureAwait(false);
        return response;
    }
Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • Removing the async on the function GetPigAsync and using Task.WaitAll solved the problem and it's running great now. – Ian Vink Jun 20 '13 at 15:55
  • Interesting, the whenall worked find in the unit tests but on iis and iis express subsequent code was never called. ConfigureAwait(false) solved the problem. Thanks – JimSTAT May 30 '17 at 13:17
  • You should probably try to find what blocks the original context anyway. If you block the request thread, you don't benefit from asynchronous execution – Panagiotis Kanavos May 30 '17 at 13:20
1

@Panagiotis Kanavos 's answer got it working . Here's a cleaned summary of how I get Tasks to work in IIS using Web API for those Googler's out there

To Web.Config add

<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />

Then create the tasks and use WaitAll()

 Task<Cat> cat= GetCat();
 Task<Dog> dog= GetDog();     
 Task.WaitAll(cat,dog,pig etc);

 NowUseTheValues(cat.Result,dog.Result);

The Tasks should be plain Tasks without the async:

 private  Task<Cat> GetCat()
 {
     return Task.Run(() => {
            return new Cat();
       });
 }
Ian Vink
  • 66,960
  • 104
  • 341
  • 555
0

If your operations are long-running, IIS can kill all threads and terminate the request. Code execution timeouts can be set in web.config

alex
  • 12,464
  • 3
  • 46
  • 67
0

Try removing the async-await from your functions:

private Task<Cat> CatAsync()
{
    return Task.Run(() =>
        {
           //Use WCF to get some data from a service
        });
}

You can keep the await in the outer function. Check this for an explanation: Why does this async action hang?

Community
  • 1
  • 1
fcuesta
  • 4,429
  • 1
  • 18
  • 13
-1

com,

I got it working with your code, (using a WebRequest and Web Response - synchronous in your WCF Calls)

As soon as i made one of the WebResponse/Requests Async (where you would be doing the WCF Call), it did hang.

 private async Task<Cat> GetCatAsync()
        {
            Cat cat = new Cat(); ;
            await Task.Run(() =>
            {
                //Use WCF to get some data from a service
                var res = GetGoogleSearchHTML("cat").Result;
                using (StreamReader sr = new StreamReader(res.GetResponseStream()))
                {
                    cat.catName = sr.ReadToEnd();
                }
            });

            return cat;
        }

        public async Task<WebResponse> GetGoogleSearchHTML(string type)
        {
            System.Net.WebRequest request = System.Net.HttpWebRequest.Create(String.Format("http://www.google.com/search?noj=1&site=cat&source=hp&q={0}&oq=search",type));
            System.Net.WebResponse response = await request.GetResponseAsync();
            return response;
        }

        public class Dog {
            public string dogName { get; set; }
            }


        public class Pig
        {
            public string pigName { get; set; }
        }


        public class Cat
        {
            public string catName { get; set; }
        }


        private async Task<Dog> GetDogAsync()
        {
            Dog dog = new Dog(); 

            await Task.Run(() =>
            {
                WebResponse res = GetGoogleSearchHTML("dog").Result;
                using (StreamReader sr = new StreamReader(res.GetResponseStream()))
                {
                    dog.dogName = sr.ReadToEnd();
                }
                //Use WCF to get some data from a service
            });

            return dog;
        }

        private async Task<Pig> GetPigAsync()
        {
            Pig pig = new Pig(); ;

            await Task.Run(() =>
            {
                var res = GetGoogleSearchHTML("pig").Result;
                using(StreamReader sr = new StreamReader(res.GetResponseStream()))
                {
                    pig.pigName = sr.ReadToEnd();
                }
                //Use WCF to get some data from a service
            });

            return pig;
        }

        public async void GetTypes()
        {
         List<Task> taskList = new List<Task>() {  };


             Task<Cat> catAsync = GetCatAsync();
             Task<Dog> dogAsync = GetDogAsync();
             Task<Pig> pigAsync = GetPigAsync();

         await Task.WhenAll(catAsync , dogAsync , pigAsync );

        var cat= catAsync.Result;
        var dog= dogAsync.Result;
        var pig= pigAsync.Result;
        }



public WebApiResult GetResponses()
{

GetTypes();

return new WebApiResult();

}
Robbie Tapping
  • 2,516
  • 1
  • 17
  • 18
  • Calling Task.Run inside an async method, that calls another async method but forces it to run synchronously with a .Result call ... ? – Panagiotis Kanavos Jun 20 '13 at 09:54
  • Yes, if you combine `await` with synchronous waiting, you're usually going to get a deadlock. But there is no indication this is what the OP is actually doing, so I think this is not an answer. – svick Jun 20 '13 at 11:06