非同期関数とタスク関数

FSharp.Core 11 では、タスク相互運用、結果変換、および有界コンカレンシーのための関数が追加されます。 既存の 非同期ワークフロー と タスク式で使用します。ランタイム非同期プロトタイプは必要ありません。

非同期からの.NET操作を待機する

非同期ワークフローのキャンセル トークンを使用して.NET操作を開始するには、ファクトリをAsync.StartTaskImmediateに渡します。

open System.IO

let tryReadFile path =
    async {
        try
            let! contents =
                Async.StartTaskImmediate(fun ct -> File.ReadAllTextAsync(path, ct))
            return Some contents
        with :? FileNotFoundException ->
            return None
    }

読み取られたファイルは、ワークフローのキャンセル トークンを受け取ります。 見つからないファイルは Noneを返し、読み取りが成功すると Some contentsが返されます。 ワークフローは、取り消しやクリーンアップなど、完了を待機します。

タスクに似た値が既にある場合は、 Async.Await を使用します。 どちらの API も、障害が発生したタスクの単一の基になる例外を直接伝達します。 複数の内部例外を含む集計は集計のままです。 Async.AwaitTask は、既存の例外ラップ動作を保持します。

API サポートされている引数/結果図形
Async.Await Task<'T> -> Async<'T>; Task -> Async<unit>
Async.Await ValueTask<'T> -> Async<'T>; ValueTask -> Async<unit>
Async.Await GetAwaiter プロトコルを使用したインライン タスクのようなオーバーロード。
Async.StartTaskImmediate (CancellationToken -> Task<'T>) -> Async<'T>;非ジェネリック Task は Async<unit>を返します。
Async.StartTaskImmediate 対応する ValueTask、 ValueTask<'T>、タスクに似たファクトリ オーバーロード。
Task.startAsyncImmediate CancellationToken -> Async<'T> -> Task<'T>
Async.RunSynchronouslyImmediate Async<'T> * ?cancellationToken: CancellationToken -> 'T

Async.RunSynchronouslyImmediate は、フォアグラウンド スレッド上の Async.RunSynchronously とは異なり、呼び出し元スレッドで開始され、完了のためにブロックされます。 タイムアウト パラメーターはありません。 UI と要求スレッドで非同期コンポジションを優先します。

非同期ワークフローをタスクとして公開する

上記のファイル読み取りワークフローをタスク ベースの呼び出し元に提供するには、キャンセル トークンを受け入れ、 Task.startAsyncImmediate渡します。

let tryReadFileTask (path: string) (ct: CancellationToken) =
    tryReadFile path |> Task.startAsyncImmediate ct

ブリッジは Task<string option> を返し、キャンセルを伝達します。 最初の中断まで呼び出し元のスレッドで開始されます。後の継続では、そのスレッドを使用する必要はありません。

バインドされたコンカレンシー

Async<'T>値は、遅延作業を表します。 通常、タスクは既に開始されている操作を表します。 リソースの使用を制限するには、タスクを既に実行しているタスクではなく、ファクトリとして記述します。

open System.IO
open System.Threading

let readFiles (paths: seq<string>) (ct: CancellationToken) =
    paths
    |> Seq.map (fun path ->
        fun token -> File.ReadAllTextAsync(path, token))
    |> Task.parallelLimit 4 ct

Task.parallelLimit は、計算の最大で正の上限を同時に開始し、結果を入力順に返します。 それ以外の場合、開始順序と完了順序は指定されていません。 コンカレンシーが 1 より大きい場合、各ファクトリは、兄弟障害を通知するリンクされたトークンを受け取ります。シーケンシャル実行の場合は、指定されたトークンを直接受け取ります。

1 つの計算エラーが発生すると、その兄弟に対してキャンセルが要求されます。 操作は、連携するためにトークンを観察する必要があります。 複数の計算が失敗した場合、1 つの例外が伝達されます。どの障害が優先するかに依存しません。

Function 署名図形
Task.parallelLimit int -> CancellationToken -> seq<CancellationToken -> Task<'T>> -> Task<'T[]>
Task.parallelDoLimit int -> CancellationToken -> seq<CancellationToken -> Task<unit>> -> Task<unit>
Task.sequential CancellationToken -> seq<CancellationToken -> Task<'T>> -> Task<'T[]>
Task.sequentialDo CancellationToken -> seq<CancellationToken -> Task<unit>> -> Task<unit>
Async.parallelLimit int -> seq<Async<'T>> -> Async<'T[]>
Async.parallelDoLimit int -> seq<Async<unit>> -> Async<unit>
Async.sequentialDo seq<Async<unit>> -> Async<unit>

Async関数は、ワークフローのアンビエント キャンセル トークンをフローします。 既存の Async.Parallel (コンカレンシー制限オプションを含む) と Async.Sequential は引き続き使用できます。 Doバリアントは、完了のみが必要な場合にunit結果を公開します。 コンカレンシー制限はレート制限ではありません。高速操作では、1 秒あたりに多数の要求を生成できます。

結果を変換し、障害から回復する

Microsoft.FSharp.Control.Async、Task、およびValueTaskモジュールは、これらの修飾された関数を公開します。 M<'T> 図形の列では、F# 型の省略形ではなく、対応する計算型を表します。

修飾関数 形状
Async.result、Task.result、ValueTask.result 'T -> M<'T>
Async.map、Task.map、ValueTask.map ('T -> 'U) -> M<'T> -> M<'U>
Async.bind、Task.bind、ValueTask.bind ('T -> M<'U>) -> M<'T> -> M<'U>
Async.ignore<'T>、Task.ignore<'T>、ValueTask.ignore<'T> M<'T> -> M<unit>;明示的な入力型引数が必要です。
Async.catchWith、Task.catchWith、ValueTask.catchWith (exn -> 'T) -> M<'T> -> M<'T>
Async.catch、Task.catch、ValueTask.catch M<'T> -> M<Result<'T, exn>>
Async.empty、Task.empty、ValueTask.empty M<unit>
Task.ofValueTask ValueTask<'T> -> Task<'T>
ValueTask.ofTask Task<'T> -> ValueTask<'T>

たとえば、ファイルの内容を長さにマップし、読み取りエラーを Resultとして表します。

open System.IO
open System.Threading

let fileLength (path: string) (ct: CancellationToken) =
    File.ReadAllTextAsync(path, ct)
    |> Task.map String.length
    |> Task.catch

Report 1を含むファイルは、Ok 8を提供します。 見つからないファイルは、FileNotFoundExceptionを含むErrorを提供します。 変換ではなく成功した値を破棄するには、 ignoreを呼び出すときに入力の種類を指定します。

let completion: System.Threading.Tasks.Task<unit> =
    Task.result 42 |> Task.ignore<int>

catchWith は、非キャンセル エラーの置換値を提供します。別の非同期計算は実行されません。 catch は、 Ok または Errorを返します。 取り消しはエラー値に変換されず、復旧ハンドラーに渡されません。 Task.empty は Task<unit>であり、非ジェネリック Task.CompletedTaskではありません。

小文字の Async.catchとは異なり、既存の Async.Catch は Choiceを返します。 復旧は、呼び出しの前にスローされた同期例外ではなく、指定された計算のエラーを処理します。 非同期は遅延されたままです。タスクは熱心なままです。

ValueTask とライブラリの可用性

ValueTask API には、FSharp.Core 11 の netstandard2.1 または net10.0 ビルドが必要です。 コア ターゲットの選択を参照してください。

ValueTask の単一消費コントラクトを確認します。個別の待機またはパイプラインではなく、各値を 1 回使用します。 繰り返し待機する場合は、 Task.ofValueTask を 1 回呼び出し、結果のタスクのみを再利用します。

参照