학습
학습 경로
F#은 간결하고 성능이 뛰어나고 강력하고 실용적인 코드를 쉽게 작성할 수 있도록 하는 오픈 소스 플랫폼 간 프로그래밍 언어입니다. Web API, Desktop, IoT, 게임 등의 다양한 유형의 애플리케이션을 만들 수 있는 범용 언어입니다.
비동기 프로그래밍은 다양한 이유로 최신 애플리케이션에 필수적인 메커니즘입니다. 대부분의 개발자가 발생하는 두 가지 기본 사용 사례가 있습니다.
백그라운드 작업에는 여러 스레드의 사용률이 포함되는 경우가 많지만 비동기 및 다중 스레딩의 개념을 별도로 고려하는 것이 중요합니다. 사실, 그들은 별도의 관심사이며, 하나는 다른 암시하지 않습니다. 이 문서에서는 별도의 개념을 자세히 설명합니다.
비동기는 여러 스레드의 사용률과 독립적이라는 이전 점은 좀 더 설명할 가치가 있습니다. 때로는 관련이 있지만 서로 엄격하게 독립적인 세 가지 개념이 있습니다.
세 가지 모두 직교 개념이지만, 특히 함께 사용될 때 쉽게 수축할 수 있습니다. 예를 들어 여러 비동기 계산을 병렬로 실행해야 할 수 있습니다. 이 관계가 병렬 처리 또는 비동기에서 서로를 암시한다는 의미는 아닙니다.
"비동기"라는 단어의 어원을 고려하면 다음 두 가지가 포함됩니다.
이 두 용어를 함께 사용하면 "비동기"가 "동시에는 아님"을 의미합니다. 정말 간단하죠. 이 정의에는 동시성 또는 병렬 처리의 의미가 없습니다. 실제로도 마찬가지입니다.
실질적으로 F#의 비동기 계산은 기본 프로그램 흐름과 독립적으로 실행되도록 예약됩니다. 이 독립적인 실행은 동시성 또는 병렬 처리를 의미하지 않으며 항상 백그라운드에서 계산이 발생함을 의미하지도 않습니다. 실제로 비동기 계산은 계산의 특성과 계산이 실행되는 환경에 따라 동기적으로 실행할 수도 있습니다.
기본 방법은 비동기 계산이 기본 프로그램 흐름과 독립적이라는 것입니다. 비동기 계산이 실행되는 시기 또는 방법에 대한 보장은 거의 없지만 오케스트레이션 및 예약에 대한 몇 가지 방법이 있습니다. 이 문서의 나머지 부분에는 F# 비동기의 핵심 개념과 F#에 기본 제공되는 형식, 함수 및 식을 사용하는 방법이 설명되어 있습니다.
F#에서 비동기 프로그래밍은 비동기 계산 및 작업의 두 가지 핵심 개념을 중심으로 합니다.
Async<'T>
작업을 구성하기 시작할 수 있는 구성 가능한 비동기 계산을 나타내는 식이 있는 형식 async { }
입니다.Task<'T>
실행 중인 .NET 작업을 나타내는 식이 있는 형식 task { }
입니다.일반적으로 작업을 사용하는 .NET 라이브러리와 상호 운용하고 비동기 코드 tailcall 또는 암시적 취소 토큰 전파에 의존하지 않는 경우 새 코드에서 이상 async {…}
사용을 task {…}
고려해야 합니다.
다음 예제에서는 "비동기" 프로그래밍의 기본 개념을 확인할 수 있습니다.
open System
open System.IO
// Perform an asynchronous read of a file using 'async'
let printTotalFileBytesUsingAsync (path: string) =
async {
let! bytes = File.ReadAllBytesAsync(path) |> Async.AwaitTask
let fileName = Path.GetFileName(path)
printfn $"File {fileName} has %d{bytes.Length} bytes"
}
[<EntryPoint>]
let main argv =
printTotalFileBytesUsingAsync "path-to-file.txt"
|> Async.RunSynchronously
Console.Read() |> ignore
0
예제에서 함수는 printTotalFileBytesUsingAsync
형식 string -> Async<unit>
입니다. 함수를 호출해도 실제로 비동기 계산이 실행되지는 않습니다. 대신 비동기적으로 실행하려는 작업의 사양으로 작동하는 값을 반환 Async<unit>
합니다. 본문에서 호출 Async.AwaitTask
되어 결과를 ReadAllBytesAsync 적절한 형식으로 변환합니다.
또 다른 중요한 줄은 호출입니다 Async.RunSynchronously
. 실제로 F# 비동기 계산을 실행하려는 경우 호출해야 하는 비동기 모듈 시작 함수 중 하나입니다.
이는 C#/Visual Basic 프로그래밍 스타일 async
과 기본적인 차이점입니다. F#에서는 비동기 계산을 콜드 작업으로 간주할 수 있습니다. 실제로 실행하려면 명시적으로 시작해야 합니다. C# 또는 Visual Basic보다 비동기 작업을 훨씬 쉽게 결합하고 시퀀스할 수 있으므로 몇 가지 이점이 있습니다.
다음은 계산을 결합하여 이전 항목을 기반으로 하는 예제입니다.
open System
open System.IO
let printTotalFileBytes path =
async {
let! bytes = File.ReadAllBytesAsync(path) |> Async.AwaitTask
let fileName = Path.GetFileName(path)
printfn $"File {fileName} has %d{bytes.Length} bytes"
}
[<EntryPoint>]
let main argv =
argv
|> Seq.map printTotalFileBytes
|> Async.Parallel
|> Async.Ignore
|> Async.RunSynchronously
0
여기서 볼 수 있듯이 함수에는 main
몇 가지 요소가 더 있습니다. 개념적으로 다음을 수행합니다.
Async<unit>
퀀스로 Seq.map
변환합니다.Async<'T[]>
실행할 때 병렬로 예약하고 실행하는 printTotalFileBytes
계산을 만듭니다.Async<unit>
실행하고 결과(즉 unit[]
, )를 무시하는 값을 만듭니다.Async.RunSynchronously
을 명시적으로 실행합니다.이 프로그램이 실행되면 printTotalFileBytes
각 명령줄 인수에 대해 병렬로 실행됩니다. 비동기 계산은 프로그램 흐름과 독립적으로 실행되므로 해당 정보를 인쇄하고 실행을 완료하는 정의된 순서가 없습니다. 계산은 병렬로 예약되지만 실행 순서는 보장되지 않습니다.
Async<'T>
이미 실행 중인 작업이 아닌 작업 사양이므로 더 복잡한 변환을 쉽게 수행할 수 있습니다. 다음은 일련의 비동기 계산을 순서대로 정렬하여 연달아 실행하는 예제입니다.
let printTotalFileBytes path =
async {
let! bytes = File.ReadAllBytesAsync(path) |> Async.AwaitTask
let fileName = Path.GetFileName(path)
printfn $"File {fileName} has %d{bytes.Length} bytes"
}
[<EntryPoint>]
let main argv =
argv
|> Seq.map printTotalFileBytes
|> Async.Sequential
|> Async.Ignore
|> Async.RunSynchronously
|> ignore
이렇게 하면 병렬로 예약하지 않고 요소의 argv
순서대로 실행되도록 예약 printTotalFileBytes
됩니다. 각 연속 작업은 이전 계산 실행이 완료될 때까지 예약되지 않으므로 계산은 실행에서 겹치지 않도록 시퀀싱됩니다.
F#에서 비동기 코드를 작성하는 경우 일반적으로 계산 일정을 처리하는 프레임워크와 상호 작용합니다. 그러나 항상 그런 것은 아니므로 비동기 작업을 예약하는 데 사용할 수 있는 다양한 함수를 이해하는 것이 좋습니다.
F# 비동기 계산은 이미 실행 중인 작업의 표현이 아닌 작업 사양 이므로 시작 함수로 명시적으로 시작해야 합니다. 다양한 컨텍스트에서 유용한 많은 비동기 시작 메서드 가 있습니다. 다음 섹션에서는 몇 가지 일반적인 시작 함수에 대해 설명합니다.
비동기 계산 내에서 자식 계산을 시작합니다. 이렇게 하면 여러 비동기 계산을 동시에 실행할 수 있습니다. 자식 계산은 부모 계산과 취소 토큰을 공유합니다. 부모 계산이 취소되면 자식 계산도 취소됩니다.
서명:
computation: Async<'T> * ?millisecondsTimeout: int -> Async<Async<'T>>
사용해야 하는 경우:
주의해야 할 사항:
Async.StartChild
사용하여 시작하는 것은 병렬로 예약하는 것과 동일하지 않습니다. 계산을 병렬로 예약하려면 .를 사용합니다 Async.Parallel
.현재 운영 체제 스레드에서 즉시 시작하여 비동기 계산을 실행합니다. 이는 계산 중에 호출 스레드에서 무언가를 업데이트해야 하는 경우에 유용합니다. 예를 들어 비동기 계산에서 진행률 표시줄 Async.StartImmediate
업데이트와 같은 UI를 업데이트해야 하는 경우 사용해야 합니다.
서명:
computation: Async<unit> * ?cancellationToken: CancellationToken -> unit
사용해야 하는 경우:
주의해야 할 사항:
Async.StartImmediate
사용하기가 부적절할 수 있습니다.스레드 풀에서 계산을 실행합니다. Task<TResult> 계산이 종료되면 해당 상태에서 완료되는 값을 반환합니다(결과를 생성하거나 예외를 throw하거나 취소됨). 취소 토큰이 제공되지 않으면 기본 취소 토큰이 사용됩니다.
서명:
computation: Async<'T> * ?taskCreationOptions: TaskCreationOptions * ?cancellationToken: CancellationToken -> Task<'T>
사용해야 하는 경우:
주의해야 할 사항:
Task
개체를 할당하므로 자주 사용되는 경우 오버헤드가 증가할 수 있습니다.비동기 계산 시퀀스를 병렬로 실행하도록 예약하여 제공된 순서대로 결과 배열을 생성합니다. 매개 변수를 지정하여 병렬 처리 수준을 선택적으로 튜닝/제한할 수 있습니다 maxDegreeOfParallelism
.
서명:
computations: seq<Async<'T>> * ?maxDegreeOfParallelism: int -> Async<'T[]>
사용 시기:
주의해야 할 사항:
전달되는 순서대로 실행할 비동기 계산 시퀀스를 예약합니다. 첫 번째 계산이 실행되고 다음 계산이 실행됩니다. 계산은 병렬로 실행되지 않습니다.
서명:
computations: seq<Async<'T>> -> Async<'T[]>
사용 시기:
주의해야 할 사항:
지정된 Task<TResult> 계산이 완료될 때까지 기다렸다가 결과를 반환하는 비동기 계산을 반환합니다. Async<'T>
서명:
task: Task<'T> -> Async<'T>
사용해야 하는 경우:
주의해야 할 사항:
지정된 Async<'T>
계산을 실행하고 반환하는 비동기 계산을 Async<Choice<'T, exn>>
만듭니다. 지정된 Async<'T>
작업이 성공적으로 Choice1Of2
완료되면 결과 값과 함께 반환됩니다. 예외가 완료 Choice2of2
되기 전에 throw된 경우 예외가 발생하면서 반환됩니다. 자체적으로 많은 계산으로 구성된 비동기 계산에 사용되고 이러한 계산 중 하나가 예외를 throw하는 경우 포괄 계산이 완전히 중지됩니다.
서명:
computation: Async<'T> -> Async<Choice<'T, exn>>
사용해야 하는 경우:
주의해야 할 사항:
지정된 계산을 실행하지만 결과를 삭제하는 비동기 계산을 만듭니다.
서명:
computation: Async<'T> -> Async<unit>
사용해야 하는 경우:
ignore
함수와 유사합니다.주의해야 할 사항:
Async<unit>
다른 함수를 사용해야 Async.Start
Async.Ignore
하는 경우 dis카드 결과가 괜찮은지 고려합니다. 형식 서명에 맞게 결과를 카드 않도록 합니다.비동기 계산을 실행하고 호출 스레드에서 결과를 기다립니다. 계산에서 예외를 생성할 경우 예외를 전파합니다. 이 호출이 차단되고 있습니다.
서명:
computation: Async<'T> * ?timeout: int * ?cancellationToken: CancellationToken -> 'T
사용 시기:
주의해야 할 사항:
Async.RunSynchronously
은 실행이 완료될 때까지 호출 스레드를 차단합니다.스레드 풀에서 반환 unit
되는 비동기 계산을 시작합니다. 완료될 때까지 기다리거나 예외 결과를 관찰하지 않습니다. 시작된 중첩된 계산은 해당 계산을 호출한 부모 계산과 Async.Start
독립적으로 시작됩니다. 해당 수명은 부모 계산에 연결되지 않습니다. 부모 계산이 취소되면 자식 계산이 취소되지 않습니다.
서명:
computation: Async<unit> * ?cancellationToken: CancellationToken -> unit
다음 경우에만 사용합니다.
주의해야 할 사항:
Async.Start
예외는 호출자에게 전파되지 않습니다. 호출 스택이 완전히 해제됩니다.Async.Start
모든 작업(예: 호출printfn
)은 프로그램 실행의 기본 스레드에 영향을 주지 않습니다.프로그래밍을 사용하는 async { }
경우 비동기/await 스타일 비동기 프로그래밍을 사용하는 .NET 라이브러리 또는 C# 코드베이스와 상호 운용해야 할 수 있습니다. C# 및 대부분의 .NET 라이브러리는 해당 및 형식을 핵심 추상화로 사용 Task<TResult>Task 하므로 F# 비동기 코드를 작성하는 방법이 바뀔 수 있습니다.
한 가지 옵션은 .NET 작업을 직접 사용하여 task { }
작성으로 전환하는 것입니다. 또는 함수를 Async.AwaitTask
사용하여 .NET 비동기 계산을 대기할 수 있습니다.
let getValueFromLibrary param =
async {
let! value = DotNetLibrary.GetValueAsync param |> Async.AwaitTask
return value
}
이 함수를 Async.StartAsTask
사용하여 .NET 호출자에게 비동기 계산을 전달할 수 있습니다.
let computationForCaller param =
async {
let! result = getAsyncResult param
return result
} |> Async.StartAsTask
사용하는 API(즉, 값을 반환하지 않는 .NET 비동기 계산)를 사용 Task 하려면 다음으로 변환할 추가 함수를 Async<'T>
추가해야 할 Task수 있습니다.
module Async =
// Async<unit> -> Task
let startTaskFromAsyncUnit (comp: Async<unit>) =
Async.StartAsTask comp :> Task
이미 Async.AwaitTask
입력으로 허용하는 항목이 Task 있습니다. 이 함수와 이전에 정의된 startTaskFromAsyncUnit
함수를 사용하면 F# 비동기 계산에서 형식을 시작하고 대기 Task 할 수 있습니다.
F#에서는 다음을 사용하여 task { }
작업을 직접 작성할 수 있습니다.
open System
open System.IO
/// Perform an asynchronous read of a file using 'task'
let printTotalFileBytesUsingTasks (path: string) =
task {
let! bytes = File.ReadAllBytesAsync(path)
let fileName = Path.GetFileName(path)
printfn $"File {fileName} has %d{bytes.Length} bytes"
}
[<EntryPoint>]
let main argv =
let task = printTotalFileBytesUsingTasks "path-to-file.txt"
task.Wait()
Console.Read() |> ignore
0
예제에서 함수는 printTotalFileBytesUsingTasks
형식 string -> Task<unit>
입니다. 함수 호출이 작업을 실행하기 시작합니다.
태스크가 완료되기 task.Wait()
를 기다리는 호출입니다.
스레딩은 이 문서 전체에서 멘션 있지만 기억해야 할 두 가지 중요한 사항이 있습니다.
예를 들어 계산은 작업의 특성에 따라 호출자의 스레드에서 실제로 실행될 수 있습니다. 또한 계산은 스레드 간에 "점프"하여 "대기 중" 기간(예: 네트워크 호출이 전송 중일 때) 사이에 유용한 작업을 수행하는 데 약간의 시간 동안 스레드를 대여할 수 있습니다.
F#은 현재 스레드에서 비동기 계산을 시작할 수 있는 몇 가지 기능을 제공하지만(또는 현재 스레드에서는 명시적으로 시작하지 않음) 비동기는 일반적으로 특정 스레딩 전략과 연결되지 않습니다.
.NET 피드백
.NET은(는) 오픈 소스 프로젝트입니다. 다음 링크를 선택하여 피드백을 제공해 주세요.
학습
학습 경로
F#은 간결하고 성능이 뛰어나고 강력하고 실용적인 코드를 쉽게 작성할 수 있도록 하는 오픈 소스 플랫폼 간 프로그래밍 언어입니다. Web API, Desktop, IoT, 게임 등의 다양한 유형의 애플리케이션을 만들 수 있는 범용 언어입니다.