2017-12-01 31 views
4

다음 코드는 오류가 발생하면 작업을 시도하고 다시 시도합니다. 콘솔에 오류를 기록하는 함수를 호출하는 오류 콜백도 있습니다.비동기 메서드에서 로거 객체에 동기식으로 액세스 할 때 교착 상태가 발생합니다.

실행하면 콘솔에 오류가 한 번만 기록되고 프로그램이 일시 중지되면 주 스레드는 retryErrorForever에서 차단되고 작업자 스레드는 차단되어 log이됩니다.

하지만 왜?

module Program 

type MyLogger(loggerName: string) = 
    member __.Warn fmt = 
     Printf.kprintf (printfn "%s: %s" loggerName) fmt 

let log = MyLogger("Logger") 

let retryErrorForever errorCallback retryTimeout work = 
    let rec loop() = async { 
     let! result = work 

     match result with 
     | Error e -> 
      errorCallback e 
      do! Async.Sleep retryTimeout 
      return! loop() 
     | Ok x -> return Ok x 
    } 

    loop() 

let retryWorkUntilOk logError timeout (work: unit -> Result<string, string>) = 
    let workflow = async { 
     return work() 
    } 

    let result = 
     retryErrorForever logError timeout workflow 
     |> Async.RunSynchronously 

    match result with 
    | Ok x -> x 
    | Error e -> failwith <| sprintf "Cannot doWork: %s" e 

let logError error = 
    log.Warn "%s" error 

let doWork work = 
    retryWorkUntilOk logError 1000 work 

let errorOnly() : Result<string, string> = 
    Error "You have no power here" 

let result = doWork errorOnly 

[<EntryPoint>] 
let main _ = 
    printfn "%A" result 
    0 

답변

5

대화 형으로 실행될 때이 프로그램은 수행해야 할 작업을 정확히 수행합니다. 매초마다 영원히 "당신은 힘이 없습니다"라고 인쇄합니다.

그러나 컴파일하고 실행 파일을 실행하는 경우 이 실행되기 전에 라인 let result = ...이 어셈블리 초기화시 평가되고 전체 프로그램 내용이 그 시간 동안 실행됩니다. 그러나 어셈블리 초기화는 동기식이라고 가정합니다. 비동기 작업을 시작할 수는 있지만 정적 초기화가 완료 될 때까지 완료되지 않으며 프로그램 작성 방법에 따라 비동기 작업이 완료 될 때까지 정적 초기화가 대기합니다. 그래서 교착 상태가됩니다.

이 문제를 해결하려면 정적 초기화가 아닌 main에서 전체 작업을 실행하면됩니다. 이것은 main에서 호출 한 후 result 기능을 만들고 수행 할 수 있습니다 그것을 설명

let result() = doWork errorOnly 

[<EntryPoint>] 
let main _ = 
    printfn "%A" <| result() 
    0 
+0

합니다. 또한 결과를 '게으른'것으로 만드는 것이 도움이됩니다. 고맙습니다 –