Как правильно ждать задачи внутри задачи? - PullRequest
0 голосов
/ 28 октября 2018

Я использую Flurl для отправки запросов.В настоящее время у меня есть следующий код, и все работает нормально:

public class Request()
{
    public async Task<Bitmap> GetImage(string url)
    {
        using (var client = new Url(url)
        {
            var content = await client.GetStringAsync();
            //return as image
        }
    }
}

И в основном потоке пользовательского интерфейса я делаю:

public async void GetImages()
{
    for (int i = 0; i < someCounter; i++)
    {
        var img = await myRequest.GetImage(url);
        Thread.Sleep(100);
    }
}

Выше работает, но поток пользовательского интерфейса спит некоторое время иЯ хотел сделать вышеуказанное в рамках задачи.Итак, я сделал:

public async void GetImages()
{
    await Task.Run(async () => 
    {
        for (int i = 0; i < someCounter; i++)
        {
            var img = await myRequest.GetImage(url);
            Thread.Sleep(100);
        }
    });
}

Однако, с учетом вышеизложенного код останавливается на await client.GetStringAsync();.Там нет исключений, код либо останавливает выполнение там, либо ждет чего-то.

Это то, что я пытаюсь исправить, или есть лучший способ.Если это правильно, почему внутреннее ожидание никогда не завершается?

Ответы [ 2 ]

0 голосов
/ 28 октября 2018

Если вы хотите использовать сон, вам, вероятно, следует использовать Task.Delay.Эта запись предоставит вам более полное представление о том, когда использовать Thread.Sleep и когда использовать Task.Delay.

Второй , в вашем коде, Что вам следуетdo вместо await и sleep имеет следующий вид:

public async void GetImages()
{
   var tasks = new List<Task>();
   for (int i = 0; i < someCounter; i++)
   {
      tasks.Add(myRequest.GetImage(url));
   }
   await Task.WhenAll(tasks).ConfigureAwait(false);
}

Обратите внимание на вызов WhenAll в конце цикла for, который создаст задачу, которая будетзавершить, когда все поставленные задачи завершены.

Затем ConfigureAwait(false) сообщает ожидающему, что вам не нужно возобновлять работу в текущем контексте (в данном случае «в текущем контексте» означает «вПоток пользовательского интерфейса ")

Третий , при вызове URL измените его на Task.Run:

public class Request()
{
    public async Task<Bitmap> GetImage(string url)
    {
        using (var client = new Url(url)
        {
            //return as image
            var content = await Task.Run(() => client.GetStringAsync());
        }
    }
}

, который вы используете Task.Run для вызова метода, а не как часть реализации способа.

0 голосов
/ 28 октября 2018

Не используйте Thread.Sleep(100);, он не будет await, он не вернет поток, он просто будет сидеть, ничего не делая и удерживая ваш UI

Если что-нибудь, вы должны использовать Task.Delay, он будет хорошо играть с async, он не будет блокировать поток UI и работает на CallBack .

Кроме того, вы должны позволить вашему шаблону async await распространяться как вирус вплоть до события, которое начало это, возвращая async Task (и только затем используя async void)

public async Task GetImages()
{
    for (int i = 0; i < someCounter; i++)
    {
        var img = await myRequest.GetImage(url);
        // for your small wait.
        await Task.Delay(100);
    }
}

Если вас беспокоят все конечные автоматы, в некоторых случаях вы можете просто вернуть Task, а не await.

Кроме того, в вашей базовой библиотеке нет смысла возвращаться кзахваченный контекст, поэтому его можно назвать ConfigureAwait(false);, это даст вам небольшую эффективность

var content = await client.GetStringAsync()
                          .ConfigureAwait(false);
...