비동기 워크플로(F#)

이 항목에서는 다른 작업의 실행을 차단하지 않고 비동기적으로 계산을 수행할 수 있도록 하는 F#의 지원 기능을 설명합니다. 예를 들어 응용 프로그램에서 다른 작업을 수행하는 동안 UI가 사용자의 조작에 응답하도록 대기 상태를 유지하는 응용 프로그램을 작성하는 데 비동기 계산을 사용할 수 있습니다.

async { expression }

설명

위 구문에서 expression으로 표시되는 계산은 비동기 sleep 작업인 I/O와 다른 비동기 작업이 수행될 때 현재 계산 스레드를 차단하지 않고 비동기 방식으로 실행하도록 설정됩니다. 현재 스레드에서 실행을 계속하는 동안 비동기 계산이 백그라운드 스레드에서 자주 시작됩니다. 식의 형식은 Async<'a>입니다. 여기서 'a는 return 키워드를 사용할 때 식을 통해 반환되는 형식입니다. 이와 같은 식의 코드를 비동기적 블록 또는 비동기 블록이라고 합니다.

비동기적인 프로그래밍 방식에는 여러 가지가 있으며 Async 클래스에서는 다양한 시나리오를 지원하는 메서드를 제공합니다. 일반적인 방법은 비동기적으로 실행할 하나 이상의 계산을 나타내는 Async 개체를 만든 다음 트리거 함수 중 하나를 사용하여 이러한 계산을 시작하는 것입니다. 트리거 함수에는 여러 가지가 있으며 비동기 계산을 실행하는 방법도 함수마다 서로 다릅니다. 어떤 함수를 사용할지는 현재 스레드, 백그라운드 스레드 또는 .NET Framework 작업 개체의 사용 여부와 계산이 완료되어도 계속 실행해야 할 함수가 있는지 여부에 따라 결정됩니다. 예를 들어 현재 스레드에서 비동기 계산을 시작하려면 Async.StartImmediate를 사용하면 됩니다. UI 스레드에서 비동기 계산을 시작하면 키 입력이나 마우스 조작 등과 같은 사용자 작업을 처리하는 기본 이벤트 루프가 차단되지 않으므로 응용 프로그램이 계속하여 응답 가능한 상태를 유지합니다.

let!을 사용한 비동기 바인딩

비동기 워크플로에서 식과 작업 중 일부는 동기적인 반면 다른 일부는 계산에 시간이 더 오래 걸리기 때문이 결과를 비동기적으로 반환하도록 설계됩니다. 메서드를 비동기적으로 호출하려면 일반적인 let 바인딩 대신 let!을 사용해야 합니다. let!을 사용하면 계산을 수행하는 동안 다른 계산이나 스레드의 실행을 계속 진행할 수 있습니다. let! 바인딩의 우변이 반환된 후 나머지 비동기 워크플로의 실행이 다시 시작됩니다.

다음 코드에서는 let과 let!이 서로 어떻게 다른지 보여 줍니다. let을 사용하는 코드 줄에서는 나중에 Async.StartImmediate 또는 Async.RunSynchronously 등을 사용하여 실행할 수 있는 개체로 비동기 계산을 만들 뿐입니다. let!을 사용하는 코드 줄에서는 계산을 시작한 다음 결과를 얻을 때까지 스레드를 일시 중지하고 결과를 얻은 시점에서 실행을 계속 진행합니다.

// let just stores the result as an asynchronous operation.
let (result1 : Async<byte[]>) = stream.AsyncRead(bufferSize)
// let! completes the asynchronous operation and returns the data.
let! (result2 : byte[])  = stream.AsyncRead(bufferSize)

let! 이외에도 use!를 사용하여 비동기 바인딩을 수행할 수 있습니다. let!과 use!의 차이는 let과 use의 차이와 같습니다. use!에 대해 현재 범위를 닫으면 개체가 삭제됩니다. F# 언어의 현재 릴리스에서 use!는 초기화할 값으로 Null을 허용하지 않습니다. 이는 use인 경우에도 마찬가지입니다.

비동기 기본 형식

단일 비동기 작업을 수행하고 결과를 반환하는 메서드를 비동기 기본 형식이라고 합니다. 이는 let!과 함께 사용할 목적으로 특별히 디자인되었습니다. F# 핵심 라이브러리에는 여러 가지 비동기 기본 형식이 정의되어 있습니다. Microsoft.FSharp.Control.WebExtensions 모듈에 웹 응용 프로그램용으로 정의되어 있는 그와 같은 두 가지 메서드로 WebRequest.AsyncGetResponseWebClient.AsyncDownloadString이 있습니다. 이들 두 기본 형식은 URL이 주어졌을 때 웹 페이지에서 데이터를 다운로드합니다. AsyncGetResponse는 WebResponse 개체를 생성하고, AsyncDownloadString은 웹 페이지의 HTML을 나타내는 문자열을 생성합니다.

Microsoft.FSharp.Control.CommonExtensions 모듈에는 비동기 I/O 작업을 위한 여러 가지 기본 형식이 포함되어 있습니다. 여기에 해당하는 Stream 클래스의 확장 메서드로는 Stream.AsyncReadStream.AsyncWrite가 있습니다.

F# PowerPack에서도 비동기 기본 형식을 추가로 제공합니다. 본문 전체가 비동기 블록 안에 포함된 함수를 정의하여 자신만의 비동기 기본 형식을 작성할 수도 있습니다.

다른 비동기 모델을 위해 디자인된 .NET Framework의 비동기 메서드를 F# 비동기 프로그래밍 모델에 사용하려면 F# Async 개체를 반환하는 함수를 만들면 됩니다. F# 라이브러리에 있는 함수를 사용하면 이 작업을 쉽게 수행할 수 있습니다.

다음은 비동기 워크플로를 사용하는 예입니다. Async 클래스의 메서드에 대한 문서에 다른 많은 예가 있습니다.

예제

이 예제에서는 비동기 워크플로를 사용하여 병렬로 계산을 수행하는 방법을 보여 줍니다.

다음 코드 예제에서 fetchAsync 함수는 웹 요청으로부터 반환된 HTML 텍스트를 가져옵니다. fetchAsync 함수에는 비동기 코드 블록이 포함되어 있습니다. 비동기 기본 형식(이 예제의 경우 AsyncDownloadString)의 결과에 대해 바인딩을 수행할 때는 let 대신 let!이 사용됩니다.

Async.RunSynchronously 함수를 사용하여 비동기 작업을 실행하고 그 결과를 기다립니다. 예를 들어 Async.Parallel 함수를 Async.RunSynchronously 함수와 함께 사용하여 여러 개의 비동기 작업을 병렬로 실행할 수 있습니다. Async.Parallel 함수는 Async 개체의 목록을 취하고, 병렬로 실행할 각 Async 작업 개체에 대한 코드를 설정하고, 병렬 계산을 나타내는 Async 개체를 반환합니다. 단일 작업의 경우와 마찬가지로 실행을 시작하려면 Async.RunSynchronously를 호출합니다.

runAll 함수는 세 개의 비동기 워크플로를 병렬로 시작하고 워크플로가 모두 완료될 때까지 대기합니다.

open System.Net
open Microsoft.FSharp.Control.WebExtensions

let urlList = [ "Microsoft.com", "https://www.microsoft.com/" 
                "MSDN", "https://msdn.microsoft.com/" 
                "Bing", "https://www.bing.com"
              ]

let fetchAsync(name, url:string) =
    async { 
        try 
            let uri = new System.Uri(url)
            let webClient = new WebClient()
            let! html = webClient.AsyncDownloadString(uri)
            printfn "Read %d characters for %s" html.Length name
        with
            | ex -> printfn "%s" (ex.Message);
    }

let runAll() =
    urlList
    |> Seq.map fetchAsync
    |> Async.Parallel 
    |> Async.RunSynchronously
    |> ignore

runAll()

참고 항목

참조

Control.Async 클래스(F#)

기타 리소스

F# 언어 참조

계산 식(F#)