Асинхронная задача приводит к зависанию программы - PullRequest
0 голосов
/ 21 апреля 2020

У меня есть задача, которая должна вызвать HTTP-запрос к серверу, и я делаю это так:

public static async Task<BoundingBox> Transform(this BoundingBox boundingBox, string epsg) {
    ...
    var min = _httpClient.GetStringAsync("https://epsg.io/trans?x=435951&y=5549182&s_srs=25832&t_srs=3857");
    var max =  _httpClient.GetStringAsync("https://epsg.io/trans?x=435911&y=5549122&s_srs=25832&t_srs=3857");
    await Task.WhenAll(min, max);
    ...
}
priorityBb = bb.Transform("epsg:3857").GetAwaiter().GetResult();

Но это заставляет мой пользовательский интерфейс зависать.

Что такое не так с моим кодом? Большое спасибо за ваш комментарий.

Ответы [ 2 ]

3 голосов
/ 21 апреля 2020

Вы должны дождаться метода Transform, потому что возвращаемая задача, вероятно, еще не выполнена вместо GetAwaiter().GetResult(). Вам, вероятно, никогда не понадобится использовать эти методы. GetResult() заблокирует текущий поток, пока задача не выполнена.

Это грубый набросок, у меня слишком мало информации о вашей структуре:

public class MyResults
{
    public string Min {get;set;}
    public string Max {get;set;}
}

public static async Task<MyResults> Transform(this BoundingBox boundingBox, string epsg) {
    ...
    var minTask = _httpClient.GetStringAsync("https://epsg.io/trans?x=435951&y=5549182&s_srs=25832&t_srs=3857");
    var maxTask =  _httpClient.GetStringAsync("https://epsg.io/trans?x=435911&y=5549122&s_srs=25832&t_srs=3857");

    await Task.WhenAll(minTask, maxTask);

    // you can access the Results now, because all tasks are completed.
    return new MyResults { Min = minTask.Result, Max = minTask.Result };
}


public static async Task GetMyData()
{
    var myResults = await bb.Transform(".....");
    //              ^^^^^

    Console.WriteLine(myResults.Min);
    Console.WriteLine(myResults.Max);
}

Если вызывающая сторона не поддерживает asyn c, вы можете попробовать что-то вроде: (не проверял, поэтому вам придется проверить)

TaskScheduler.FromCurrentSynchronizationContext() нужен только тогда, когда вы имеете дело с (например) поток пользовательского интерфейса.

public static void GetMyData()
{
    // You are not able to await it here. Fire and "forget"

    Task.Run<MyResults>(() =>
    {
        // Not executed on the UI thread
        return bb.Transform(".....");

    })
    .ContinueWith(transformTask =>
    {
        // back on the UI thread.....
        var myResults = transformTask.Result;

        Console.WriteLine(myResults.Min);
        Console.WriteLine(myResults.Max);

    }, TaskScheduler.FromCurrentSynchronizationContext());
}
2 голосов
/ 21 апреля 2020
priorityBb = bb.Transform("epsg:3857").GetAwaiter().GetResult();

Эта строка блокирует ваш поток пользовательского интерфейса из-за вызова GetResult. Раньше вы правильно использовали async / await, но в этой строке вы смешиваете асин c код с кодом блокировки. Вы должны использовать тот же подход, что и в методе Transform и await в результате вместо блокировки, используя GetResult

. Чтобы исправить это, просто измените эту строку на

priorityBb = await bb.Transform("epsg:3857");

Используя GetResult подобное может привести к тупикам, и в большинстве случаев это не очень хорошая идея. Всякий раз, когда вы можете просто набрать async / await.

Если вы не можете позвонить async, то ваш звонок не может быть async. async / await, вероятно, является лучшим подходом здесь, но если по какой-то причине вы не можете его использовать, вы можете дождаться ответа и обработать его в другом потоке, используя Task.Run

...