Async.Start vs Async.StartChild

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

async {
    //(...async stuff...)
    for msg in msgs do 
        asyncSendMsg msg |> Async.Start
    //(...more async stuff...)
}

и

async {
    //(...async stuff...)
    for msg in msgs do 
        let! child = asyncSendMsg msg |> Async.StartChild
        ()
    //(...more async stuff...)
}

Ответ 1

Ключевое различие заключается в том, что при запуске рабочего процесса с Async.StartChild он будет делиться маркером отмены с родительским. Если вы отмените родителя, все дети также будут отменены. Если вы запустите дочерний элемент с помощью Async.Start, то это полностью независимый рабочий процесс.

Вот минимальный пример, демонстрирующий разницу:

// Wait 2 seconds and then print 'finished'
let work i = async {
  do! Async.Sleep(2000)
  printfn "work finished %d" i }

let main = async { 
    for i in 0 .. 5 do
      // (1) Start an independent async workflow:
      work i |> Async.Start
      // (2) Start the workflow as a child computation:
      do! work i |> Async.StartChild |> Async.Ignore 
  }

// Start the computation, wait 1 second and than cancel it
let cts = new System.Threading.CancellationTokenSource()
Async.Start(main, cts.Token)
System.Threading.Thread.Sleep(1000)    
cts.Cancel()

В этом примере, если вы начнете вычисление с помощью (1), то все рабочие элементы завершатся и печатаются через 2 секунды. Если вы используете (2), все они будут отменены, когда основной рабочий процесс будет отменен.