асинхронное ожидание проблем порядка выполнения с httpclient - PullRequest
0 голосов
/ 08 июня 2018

В нашем приложении мы используем асинхронные звонки.Эти звонки нам нужно ждать, поэтому мы используем await.Но мы замечаем, что приложение продолжает приложение, где-то в ожидании от HttpClient.SendAsync.Мы воспроизвели его с помощью следующего кода: -

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace AsyncExperiment
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("1");
            var adapter = new Adapter();
            Console.WriteLine("2");
            var result = Task.Factory.StartNew(() => adapter.Start()).Result;
            Console.WriteLine("21");
            Console.ReadKey();
        }
    }

    public class Adapter
    {
        public async Task<string> Start()
        {
            Console.WriteLine("3");
            return await CollectionAccessor.ExecuteWithinScope(async collection => {
                Console.WriteLine("8");
                var adapter = new AsyncSearchAdapter();
                Console.WriteLine("9");
                var result = await adapter.GetSearchAsync();
                Console.WriteLine("19");
                Console.WriteLine(result);
                Console.WriteLine("20");
                return "";
            });    
        }
    }

    public class Client
    {

        public async Task<string> Get()
        {
            Console.WriteLine("12");
            var requestMessage = new HttpRequestMessage(HttpMethod.Get, "https://22ad5e1e-688d-4ba4-9287-6bb4a351fd05.mock.pstmn.io/test");
            Console.WriteLine("13");
            HttpClient httpClient = new HttpClient();
            Console.WriteLine("14");
            HttpResponseMessage response = await httpClient.SendAsync(requestMessage);
            Console.WriteLine("15");
            if(response.IsSuccessStatusCode){
               Console.WriteLine("16a");
               return await response.Content.ReadAsStringAsync();     
            }
            Console.WriteLine("16b");
            return null;
        }
    }

    public class AsyncSearchAdapter
    {
        public async Task<string> GetSearchAsync()
        { 
            Console.WriteLine("10");
            var client = new Client();
            Console.WriteLine("11");
            var response = await client.Get();
            Console.WriteLine("17");
            if(response.Equals("{'test', 'test'}")){
                Console.WriteLine("18a");
                return response;
            }
            Console.WriteLine("18b");
            return response;
        }
    }

    public static class CollectionAccessor
    {

        public static TReturn ExecuteWithinScope<TReturn>(Func<ICatalogCollection, TReturn> func)
        {
            Console.WriteLine("4");
            if(func == null) throw new ArgumentNullException("func");
            Console.WriteLine("5");

            using(var catalogCollection = Resolver())
            {
                Console.WriteLine("7");
                return func(catalogCollection);
            }
        }

        public static ICatalogCollection Resolver()
        {
            Console.WriteLine("6");
             return new CatalogCollection();
        }
    }

    public interface ICatalogCollection: IDisposable
    {
        string notImportant { get;}
    }

    public class CatalogCollection : ICatalogCollection, IDisposable
    {
        public string notImportant { get;}

        public CatalogCollection(){ 
            notImportant = "test";
        }

        public void Dispose()
        {
            throw new NotImplementedException();
        }
    }


}

Мы ожидаем, что порядок журналов будет

1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21

, но мы получим такой порядок:

1,2,3,4,5,6,7,8,9,10,11,12,13,14,21,15,16,17,18,19,20

Может ли кто-нибудь объяснить мне, почему это происходит в таком порядке.И как получить его в ожидаемом порядке?

Спасибо !!!

1 Ответ

0 голосов
/ 08 июня 2018

Вы запускаете асинхронную функцию (adapter.Start()) и не ожидаете ее.Попробуйте изменить

  var result = Task.Factory.StartNew(() => adapter.Start()).Result;

на

 var result = adapter.Start().Result;

или

 var result = Task.Factory.StartNew(() => adapter.Start().Result).Result;

, и я полагаю, что вы делаете ту же проблему здесь

await CollectionAccessor.ExecuteWithinScope(async collection => {...})

просто убедитесь, что CollectionAccessor.ExecuteWithinScope будет обрабатывать ожидающую переданную ему функцию.Как

async Task CollectionAccessor.ExecuteWithinScope(Func <ICollection, Task> action)
{
    ...
    await (action(collection));
    ...
}

или, по крайней мере, вернуть его

async Task CollectionAccessor.ExecuteWithinScope(Func <ICollection, Task> action)
{
    ...
    return  (action(collection));       
}

UPD

Прямо здесь

public static TReturn ExecuteWithinScope<TReturn>(Func<ICatalogCollection, TReturn> func)
{
    Console.WriteLine("4");
    if (func == null) throw new ArgumentNullException("func");
    Console.WriteLine("5");

    using (var catalogCollection = Resolver())
    {
        Console.WriteLine("7");
        return func(catalogCollection); // <<<<<<<HERE
    }
}

Вы находитесьсоздание задачи, которая еще не завершена, и вы возвращаете ее и утилизируете коллекцию до ее завершения.Я думаю, вам нужно дождаться завершения задания и только после того, как оно вернется.Например,

public static async Task<TReturn> ExecuteWithinScope<TReturn>(Func<ICatalogCollection, Task<TReturn>> func)
{
    Console.WriteLine("4");
    if (func == null) throw new ArgumentNullException("func");
    Console.WriteLine("5");

    using (var catalogCollection = Resolver())
    {
        Console.WriteLine("7");
        return await func(catalogCollection); // waiting task for completition
    }
}

ИЛИ вам нужно расположить коллекцию внутри задачи, например

public static TReturn ExecuteWithinScope<TReturn>(Func<ICatalogCollection, TReturn> func)
{
    Console.WriteLine("4");
    if (func == null) throw new ArgumentNullException("func");
    Console.WriteLine("5");

    //using (var catalogCollection = Resolver()) // not in using!
    {
        var catalogCollection = Resolver();
        Console.WriteLine("7");
        return func(catalogCollection);
    }
}

А затем

    return await CollectionAccessor.ExecuteWithinScope(async collection =>
    {
        Console.WriteLine("8");
        var adapter = new AsyncSearchAdapter();
        Console.WriteLine("9");
        var result = await adapter.GetSearchAsync();
        Console.WriteLine("19");
        Console.WriteLine(result);
        Console.WriteLine("20");

        collection.Dispose();  //// Disposing!
        return "";
    });

С моей точки зрения сначалаподход (await func(catalogCollection);) - лучший

...