2014-12-20 6 views
3

각 비동기 하위 블록을 각 루프에서 실행하는 비동기 루프가있는 비동기 워크 플로를 만들려고합니다. 그리고이 비동기 하위 블록을 취소 할 수있게하려고합니다. 그러나 취소 할 때 을 입력하지 마십시오. 메인 루프가 취소되기를 원합니다. do! subBlock 다음 줄에서 계속하기를 원합니다.F # 비동기 워크 플로의 하위 블록 취소

도 허용 서명이 내가 Async에서 볼 수있는 유일한 방법 Async.StartAsTask입니다 (CancellationToken 소요는 async로 변환 할 수있는 무언가를 반환),하지만이 취소 할 때 중지 것 같다; 아래에서 "취소됨"을 인쇄 한 다음 다른 것은 표시하지 않습니다.

open System 
open System.Threading 
open System.Threading.Tasks 

// runs until cancelled 
let subBlock = 
    async { 
    try 
     while true do 
     printfn "doing it" 
     do! Async.Sleep 1000 
     printfn "did it" 
    finally 
     printfn "cancelled!" 
    } 

[<EntryPoint>] 
let main argv = 
    let ctsRef = ref <| new CancellationTokenSource()  

    let mainBlock = 
    //calls subBlock in a loop 
    async { 
     while true do 
     ctsRef := new CancellationTokenSource() 
     do! Async.StartAsTask(subBlock, TaskCreationOptions.None, (!ctsRef).Token) 
      |> Async.AwaitTask 
     printfn "restarting" 
    } 
    Async.Start mainBlock 

    //loop to cancel CTS at each keypress 
    while true do 
    Console.ReadLine() |> ignore 
    (!ctsRef).Cancel() 
    0 

어떤 방법이 있습니까?

답변

1

자녀 작업이 취소되면 OperationCanceledExceptionmainBlock을 가져옵니다.

let rec mainBlock = 
    async { 
     ctsRef := new CancellationTokenSource() 
     let task = Async.StartAsTask(subBlock, TaskCreationOptions.None, (!ctsRef).Token) |> Async.AwaitTask 
     do! Async.TryCancelled(task, fun e -> 
      (!ctsRef).Dispose() 
      printfn "restarting" 
      Async.Start mainBlock) 
    } 

작업이 취소되고 mainBlock 명시 적으로 취소 처리기에서 다시 시작됩니다 : 나는이를 사용하여 작동시킬 수 있었다. mainBlock이 정의 내에서 사용되기 때문에 #nowarn "40"을 추가해야합니다. 또한 토큰 소스에서 처분하십시오.

twothreads에이 문제에 대한 자세한 정보 (및 더 좋은 해결책은 StartCatchCancellation 일 수 있음)를 찾을 수 있습니다.

1

작업자가 명시 적으로 지정된 취소 토큰을 통해 관리되기 때문에 작업자를 시작하고 취소하는 호출자가 비동기인지 여부도이 문제에 실제로 영향을주지 않습니다.

비동기에는 값을 반환 할 수있는 정상적인 예외, 예외를위한 예외 및 취소를위한 예외가 있습니다. Async.OnCancel, Async.TryCancelled 또는 예외 사례가 포함 된 일반 Async.FromContinuations과 같이 비동기에 취소 계속을 추가하는 여러 가지 방법이 있습니다. 이 runMain가 비동기의 경우와 마찬가지로 잘 작동

let rec doBlocks() = 
    async { printfn "doing it" 
      do! Async.Sleep 1000 
      printfn "did it" 
      do! doBlocks() } 

let rec runMain() = 
    use cts = new CancellationTokenSource() 
    let worker = Async.TryCancelled(doBlocks(), fun _ -> printfn "Cancelled") 
    Async.Start(worker, cts.Token) 
    let k = Console.ReadKey(true) 
    cts.Cancel() 
    if k.Key <> ConsoleKey.Q then runMain() 

: 여기에 원하는 출력을 가진 프로그램입니다. 이 간단한 경우에는 "취소됨"메시지 자체를 인쇄해도됩니다.

이 정보가 도움이되기를 바랍니다. 나는 프로그램을 구성하는 방법에 대한 일반적인 대답이 있다고 생각하지 않는다. 이는 구체적인 유스 케이스에 달려있다.