C# HttpClient.GetStringAsync()调用冻结所有内容而不抛出错误,但仅在特定条件下

C# HttpClient.GetStringAsync()调用冻结所有内容而不抛出错误,但仅在特定条件下,c#,multithreading,windows-runtime,async-await,win-universal-app,C#,Multithreading,Windows Runtime,Async Await,Win Universal App,我正在编写一个Windows通用应用程序,它严重依赖REST服务提供的信息。为了实现这一点(并提供缓存支持),我正在编写一个附带的库,应用程序将调用该库。代码的核心是以下功能: PokeLib.Utilities: private static HttpClient fetcher = new HttpClient(); public static async Task<JObject> GetPokemon(int n) { return

我正在编写一个Windows通用应用程序,它严重依赖REST服务提供的信息。为了实现这一点(并提供缓存支持),我正在编写一个附带的库,应用程序将调用该库。代码的核心是以下功能:

PokeLib.Utilities:

    private static HttpClient fetcher = new HttpClient();

    public static async Task<JObject> GetPokemon(int n)
    {
        return await GetData(prefix + "pokemon/" + n + "/");
    }

    public static async Task<JObject> GetType(int n)
    {
        return await GetData(prefix + "type/" + n + "/");
    }

    private static Dictionary<string, JObject> resourceCache = new Dictionary<string, JObject>(); // TODO: Stale check

    public static async Task<JObject> GetData(string resourcePath, bool forceRefresh = false)
    {
        if (!resourceCache.ContainsKey(resourcePath) || forceRefresh)
            resourceCache[resourcePath] = await FetchDataFromServer(resourcePath);

        return resourceCache[resourcePath];
    }

    public static async Task<JObject> FetchDataFromServer(string resourcePath)
    {
        try
        {
            string json = await fetcher.GetStringAsync(baseUri + resourcePath); // ***** this is the line that dies *****
            return JObject.Parse(json);
        }
        catch (Exception e)
        {
            Debug.WriteLine(e.ToString());
            return null;
        }
    }
    public Type(int id, bool constructNow = false)
    {
        this.id = id;

        try
        {
            if (constructNow) // this is being called with true
                Create().Wait();
        }
        catch (Exception e)
        {
            throw e;
        }
    }

    public override async Task Create()
    {
        JObject data = await Utilities.GetType(id);

        if (data == null)
            throw new KeyNotFoundException("Type ID #" + id + " does not exist");

        name = (string)data["name"];
        id = (int)data["id"];
        resourcePath = (string)data["resource_uri"];
    }
调用Type.cs的代码实际上与Pokemon.cs的代码完全相同(包括.Wait()),因此我认为这不是问题所在


有什么帮助/想法吗?谢谢

我对
WinRT
不太熟悉,但这看起来像是一个典型的死锁,因为
WinRTSynchronizationContext
在您的每个调用中流动

为了完全避免这种情况,可以使用
InitializeAsync
模式,因为构造函数不能是异步的

模式很简单,在构造对象之后,让调用方调用
InitializeAsync
方法:

public Type(int id, bool constructNow = false)
{
    this.id = id;
    this.constructNow = constructNow;
}

public Task InitializeAsync()
{
     return constructNow ? Create() : Task.FromResult(false);
}
然后称之为:

var type = new Type(1, true);
await type.InitializeAsync();
在为外部库实现代码时,建议使用调用
asyncwait
模式中的异步方法。它所做的是显式抑制同步上下文的流,以避免用户使用
Task.Result
Task.Wait调用异步方法时出现潜在错误:

string json = await fetcher.GetStringAsync(baseUri + resourcePath).ConfigureAwait(false);

在整个调用链中使用此选项。

完全没有关于
fetcher
的信息,代码示例中没有
GetItem()
方法?即使这些显然是你问题中最重要的部分?看啊,哎呀
GetItem()
应该是
GetType()
,而
fetcher
是一个HttpClient(正如标题所暗示的,但我应该明确提到的那样)。已修复。您是否在任何顶级方法中调用
Task.Result
Task.Wait
?发布一个如何?我使用的是.Wait()(可能不正确)作为一种打破不断冒泡的等待异步的方法。我是否应该尝试尽快放弃异步性,并将其一直保留到UI的代码?没错。异步性需要从UI事件处理程序开始,一路冒泡。不要压制它,拥抱它。酷!这似乎对我来说很重要通常是这样做的,但现在还有其他一些事情有困难,但我认为这更适合单独的帖子: