Параллельная библиотека задач против рабочих процессов Async

У меня есть материал, написанный на С#, который выполняет параллельный код, что сильно использует параллельную библиотеку задач (Целевые и будущие цепочки продолжения).

Теперь я переношу это на F # и пытаюсь выяснить плюсы и минусы использования рабочих процессов F # Async и конструкций в TPL. Я склоняюсь к TPL, но думаю, что это можно сделать в любом случае.

Есть ли у кого-нибудь советы и мудрость о написании параллельных программ в F # для публикации?

Ответ 1

Название в значительной степени суммирует разницу: асинхронное программирование против параллельного программирования. Но в F # вы можете смешивать и сопоставлять.

F # Асинхронные рабочие процессы

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

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

Параллельная библиотека задач

Параллельная библиотека задач в .NET 4.0 абстрагирует понятие задачи - например, декодирование MP3 или чтение некоторых результатов из базы данных. В этих ситуациях вы действительно хотите получить результат вычисления и в какой-то момент позже ожидаете результата операции. (Путем доступа к свойству .Result.)

Вы можете легко смешивать и сопоставлять эти понятия. Например, выполнение всех операций ввода-вывода в объекте задачи TPL. Программисту вы отвлекли необходимость "заниматься" этим дополнительным потоком, но под обложками вы тратите ресурсы.

Как мудрый, вы можете создать серию рабочих процессов a # a и выполнить их параллельно (Async.Parallel), но тогда вам нужно ждать окончательного результата (Async.RunSynchronously). Это освобождает вас от необходимости явно запускать все задачи, но на самом деле вы просто выполняете вычисления параллельно.

По моему опыту я считаю, что TPL более полезен, потому что обычно я хочу параллельно выполнять N операций. Тем не менее, рабочие процессы F # async идеальны, когда есть что-то, что происходит "за кулисами", например, типа "Реактивный агент" или "Тип почтового ящика". (Вы отправляете что-то сообщение, оно обрабатывает его и отправляет обратно.)

Надеюсь, что это поможет.

Ответ 2

В 4.0 я бы сказал:

  • Если ваша функция последовательна, используйте рабочие процессы Async. Они просто читают лучше.
  • Используйте TPL для всего остального.

Также возможно смешивать и сопоставлять. Они добавили поддержку для запуска рабочего процесса в качестве задачи и создания задач, которые следуют шаблону Begin/End async, используя TaskFactory.FromAsync, TPL эквивалент Async.FromBeginEnd или Async.BuildPrimitive.

let func() =
    let file = File.OpenRead("foo")
    let buffer = Array.zeroCreate 1024
    let task1 = Task.Factory.FromAsync(file.BeginRead(buffer, 0, buffer.Length, null, null), file.EndRead)
    task1.Start()

    let task2 = Async.StartAsTask(file.AsyncRead(1024))
    printfn "%d" task2.Result.Length

Также стоит отметить, что как среда выполнения Async Workflows, так и TPL собираются создать дополнительный примитив ядра (событие) и использовать WaitForMultipleObjects для отслеживания завершения ввода-вывода, а не для использования портов завершения и обратных вызовов. Это нежелательно в некоторых приложениях.