У меня есть такой метод:

public async Task<IEnumerable<Model>> Get([FromUri]IList<string> links)
{
    IList<Model> list = new List<Model>();
    foreach (var link in links)
    {
        MyRequestAsync request = new MyRequestAsync(link);
        list.Add(await request.GetResult());
    }

    return list;
}

Но мне просто интересно, действительно ли это async, потому что я думаю, что эта часть list.Add(await request.GetResult()); и return list; нарушает async природу метода.

Пожалуйста, поправьте меня, если я ошибаюсь, и если я прав, как я могу это исправить?

ОБНОВЛЕНО: для моего понимания мне нужно что-то вроде этого C # 5.0 async await возвращает список return await Task.Run(() => new List<string>() {"a", "b"});, но не знаю, как применить это в моем случае.

1
sreginogemoh 17 Апр 2014 в 16:47

3 ответа

Лучший ответ

Ваш метод является асинхронным, но, возможно, он не наилучшим образом использует ресурсы.

Ваш метод войдет в цикл foreach, создаст объект MyRequestAsync, а затем (в точке await) откажется от своего потока до тех пор, пока результат не станет доступным. Как только результат будет доступен, будет найден соответствующий поток, и метод продолжит работу. Он добавит результат к list и вернется к началу цикла, и будет повторять весь этот процесс снова и снова.

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

public async Task<IEnumerable<Model>> Get([FromUri]IList<string> links)
{
    IList<Task<Model>> list = new List<Task<Model>>();
    foreach (var link in links)
    {
        MyRequestAsync request = new MyRequestAsync(link);
        list.Add(request.GetResult());
    }

    return new List<Model>(await Task.WhenAll(list));
    //Or just
    //return await Task.WhenAll(list);
    //Since we don't need to return a list
}

И, из соображений глупости, вы можете переписать весь метод как:

return await Task.WhenAll(from l in links select new RequestAsync(l).GetResult());

Но это может сделать его менее читабельным.

4
Damien_The_Unbeliever 17 Апр 2014 в 13:34

На мой взгляд, ввод-вывод - это async, поэтому метод можно назвать "действительно асинхронным".
async предназначен для того, чтобы ввод-вывод не блокировал поток, когда он чего-то ждет (здесь результат), но не когда он «что-то делает» (здесь list.Add).

1
Christoph Fink 17 Апр 2014 в 12:51

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

При этом ваш код выглядит неблокирующим, потому что он использует await (вместо, скажем, Task.Wait). Слегка упрощая ситуацию, этот метод вернется при первой операции await, что, вероятно, и нужно.

0
usr 17 Апр 2014 в 12:56