Вызывает ли Task.Run накладные расходы при операции ввода-вывода? - PullRequest
2 голосов
/ 14 января 2020

Я хотел бы загружать данные асинхронным способом из различных служб через HttpClient (для тестирования я создал несколько методов с Task.Delay). Итак, сначала я решил создать список задач и в конце концов использовать await Task.WhenAll(tasks), чтобы все ждать.

Все работает так, как я хочу, но есть одна проблема, если я передаю лямбду в tasks.Add(). Я должен использовать Task.Run, потому что без него нельзя использовать await внутри.

Итак, мой вопрос:

Будет ли это Task.Run() создавать дополнительные издержки? Потому что при операции ввода-вывода мы не должны использовать Task.Run, а просто asyn c await.

class Program
{
    public static async Task<int> DownloadDelay15()
    { await Task.Delay(15000); return 15; }

    public static async Task<int> DownloadDelay2()
    { await Task.Delay(1000); return 2; }

    static async Task Main(string[] args)
    {
        var tasks = new List<Task<int>>();
        tasks.Add(DownloadDelay15());
        tasks.Add(DownloadDelay2());
        tasks.Add(Task.Run(async () => //additional overhead?
        {
            await Task.Delay(1000);
            return 6;
        }));

        var watch = Stopwatch.StartNew();
        var results = await Task.WhenAll(tasks);
        watch.Stop();
        var elapsedSeconds = watch.Elapsed.Seconds;
    }  
}

Ответы [ 3 ]

6 голосов
/ 15 января 2020

, если я передаю лямбду задачам. Add (). Я должен использовать Task.Run, потому что без него нельзя использовать await внутри.

Вы не можете передать лямбду в Add; вам нужно передать Task на Add. Вы можете сделать это, создав другой метод:

async Task<int> DownloadDelay6()
{
  await Task.Delay(1000);
  return 6;
}

var tasks = new List<Task<int>>();
tasks.Add(DownloadDelay15());
tasks.Add(DownloadDelay2());
tasks.Add(DownloadDelay6());

Будет ли этот Task.Run () генерировать дополнительные издержки? Поскольку при операции ввода-вывода мы не должны использовать Task.Run, а просто asyn c await.

Да; Task.Run запускает свой делегат в потоке пула потоков, поэтому есть некоторые дополнительные издержки. Это не много, но оно есть.

2 голосов
/ 15 января 2020

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

static async Task Main(string[] args)
{
    var jobs = new List<Func<Task<int>>>
    {
        DownloadDelay15,
        DownloadDelay2,
        async () => { await Task.Delay(1000); return 6; }
    };

    var tasks = jobs.Select(job => job()).ToList();

    var watch = Stopwatch.StartNew();
    var results = await Task.WhenAll(tasks);
    watch.Stop();
    var elapsedSeconds = watch.Elapsed.Seconds;
}

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

0 голосов
/ 15 января 2020

Если вы используете Task.Run ...

  1. Дополнительные издержки будут.
  2. Вероятно, ваша программа будет работать быстрее.

Это может показаться противоречивым. Причина того, что ваша программа будет работать быстрее, при условии, что вы запустите ее на многоядерном компьютере, заключается в том, что задачи будут запускаться параллельно. Это может быть неуместно, если ваши задачи запускаются с нулевой нагрузкой на процессор, и задачи в вашем примере такие же, но не все задачи могут быть запущены мгновенно. Некоторые задачи включают в себя синхронный код, который выполняется до запуска задачи. Без Task.Run этот синхронный код будет выполняться последовательно, поскольку одна задача будет запускаться за другой. При Task.Run синхронный код будет работать параллельно во всех доступных ядрах. Дополнительные издержки Task.Run будут оплачиваться системой в целом, а не только вашей программой. Издержки очень малы, около 2 микросекунд процессорного времени на один вызов, поэтому, скорее всего, вы не заметите никакой разницы, если только у вас не будет нескольких сотен тысяч задач для одновременного запуска.

...