Рекомендации по параллелизации с использованием асинхронного рабочего процесса

Предположим, я хотел очистить веб-страницу и извлечь некоторые данные. Я бы скорее всего написал что-то вроде этого:

let getAllHyperlinks(url:string) =
    async {  let req = WebRequest.Create(url)
             let! rsp = req.GetResponseAsync()
             use stream = rsp.GetResponseStream()             // depends on rsp
             use reader = new System.IO.StreamReader(stream)  // depends on stream
             let! data = reader.AsyncReadToEnd()              // depends on reader
             return extractAllUrls(data) }                    // depends on data

let! сообщает F # выполнить код в другом потоке, затем привязать результат к переменной и продолжить обработку. В приведенном выше примере используются два оператора let: один для получения ответа и один для чтения всех данных, поэтому он порождает по крайней мере два потока (пожалуйста, поправьте меня, если я ошибаюсь).

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

Есть ли какая-либо польза от наличия более чем одного let! в коде выше?

Если нет, как этот код должен измениться, чтобы воспользоваться несколькими операторами let!?

Ответ 1

Ключом является то, что мы не создаем новые потоки. В течение всего рабочего процесса из ThreadPool потребляется 1 или 0 активных потоков. (Исключение, вплоть до первого "!", Код запускается в пользовательском потоке, который сделал Async.Run.) "Let!" позволяет перейти от потока, пока операция Async находится в море, а затем берет поток из ThreadPool при возврате операции. Преимущество (производительность) - это меньшее давление на ThreadPool (и, конечно, основным преимуществом пользователя является простая модель программирования - в миллион раз лучше, чем все, что BeginFoo/EndFoo/обратный вызов, который вы в противном случае пишете).

См. также http://cs.hubfs.net/forums/thread/8262.aspx

Ответ 2

Я писал ответ, но Брайан победил меня. Я полностью согласен с ним.

Я хотел бы добавить, что если вы хотите распараллеливать синхронный код, правильным инструментом является PLINQ, а не async workflows, а Дон Симе объясняет.