Görev ifadeleri
Bu makalede, zaman uyumsuz ifadelere benzeyen ancak .net görevlerini doğrudan yazmanıza izin veren, for Task ifadelerinde F # desteği açıklanmaktadır. Zaman uyumsuz ifadeler gibi, görev ifadeleri kodu zaman uyumsuz olarak yürütür, diğer bir deyişle, diğer çalışmanın yürütülmesini engellemeden.
Zaman uyumsuz kod normalde zaman uyumsuz ifadeler kullanılarak yazılır. .NET görevleri oluşturan veya tüketen .NET kitaplıklarıyla kapsamlı olarak birlikte çalışırken görev ifadelerinin kullanılması tercih edilir. Görev ifadeleri Ayrıca performansı ve hata ayıklama deneyimini de iyileştirebilir. Ancak, görev ifadeleri makalenin ilerleyen kısımlarında açıklanan bazı kısıtlamalarla gelir.
Syntax
task { expression }
Önceki sözdiziminde, tarafından temsil edilen hesaplama expression .net görevi olarak çalışacak şekilde ayarlanır. Görev, ilk zaman uyumsuz işlem gerçekleştirilene kadar (örneğin, zaman uyumsuz bir uyku, zaman uyumsuz g/ç veya diğer temel zaman uyumsuz işlem), bu kod yürütüldükten ve geçerli iş parçacığında çalıştıktan sonra başlatılır. İfadenin türü, Task<'T> , 'T return anahtar sözcüğü kullanıldığında ifade tarafından döndürülen türdür.
Let kullanarak bağlama!
Bir görev ifadesinde bazı ifadeler ve işlemler zaman uyumludur ve bazıları zaman uyumsuzdur. Sıradan bağlama yerine zaman uyumsuz bir işlemin sonucunu beklekdığınızda let kullanırsınız let! . ' Nin etkisi, let! hesaplamanın gerçekleştirildiği için yürütmenin diğer hesaplamalar veya iş parçacıklarında devam etmesine olanak tanır. Bağlamanın sağ tarafı let! döndüğünde, görevin geri kalanı yürütmeyi sürdürür.
Aşağıdaki kod ve arasındaki farkı gösterir let let! . Tarafından kullanılan kod satırı, let daha sonra, örneğin veya kullanarak daha sonra dönebilmeniz için bir nesne olarak bir görevi oluşturur task.Wait() task.Result . Tarafından kullanılan kod satırı let! görevi başlatır ve sonucunu bekler.
// let just stores the result as an task.
let (result1 : Task<int>) = stream.ReadAsync(buffer, offset, count, cancellationToken)
// let! completes the asynchronous operation and returns the data.
let! (result2 : int) = stream.ReadAsync(buffer, offset, count, cancellationToken)
F # task { } ifadeleri aşağıdaki zaman uyumsuz işlem türlerini bekleedebilir:
- .NET görevleri Task<TResult> ve genel olmayan Task .
- .NET değer görevleri ValueTask<TResult> ve genel olmayan ValueTask .
- F # zaman uyumsuz hesaplamalar
Async<T>. - F # RFC FS-1097' de belirtilen "GetAwaiter" örüntüsünün ardından herhangi bir nesne.
return ifadelerde
Görev ifadeleri içinde, return expr bir görevin sonucunu döndürmek için kullanılır.
return! ifadelerde
Görev ifadeleri içinde, return! expr başka bir görevin sonucunu döndürmek için kullanılır. Bu, let! öğesini kullanarak ve sonra sonucun hemen döndürülmesinden eşdeğerdir.
Denetim akışı
Görev ifadeleri denetim akışı yapılarını,,,, ve ' i içerebilir for .. in .. do while .. do try .. with .. try .. finally .. if .. then .. else if .. then .. . Bunlar, with zaman uyumlu olarak yürütülen ve işleyicileri dışında daha fazla görev yapıları içerebilir finally . Zaman uyumsuz olması gerekiyorsa try .. finally .. , use türünde bir nesne ile birlikte bir bağlama kullanın IAsyncDisposable .
use ve use! bağlamalar
Görev ifadeleri içinde use bağlamalar, veya türündeki değerlere bağlanabilir IDisposable IAsyncDisposable . İkinci için, elden çıkarma temizleme işlemi zaman uyumsuz olarak yürütülür.
' A ek olarak let! , use! zaman uyumsuz bağlamalar gerçekleştirmek için kullanabilirsiniz. Ve arasındaki fark let! , use! ve arasındaki farklılığı ile aynıdır let use . İçin use! , nesnesi geçerli kapsamın kapandığı sırada atıldı. F # 6 ' da, use! bir değerin null olarak başlatılmasına, ancak olsa da izin vermediğini unutmayın use .
Değer görevleri
Değer görevleri, görev tabanlı programlamada ayırmaları önlemek için kullanılan yapı birimleridir. Değer görevi, kullanılarak gerçek bir göreve açılmış olan kısa ömürlü bir değerdir .AsTask() .
Bir görev ifadesinden bir değer görevi oluşturmak için |> ValueTask<ReturnType> veya kullanın |> ValueTask . Örnek:
let makeTask() =
task { return 1 }
makeTask() |> ValueTask<int>
İptal belirteçleri ve iptal denetimleri ekleme
F # Async ifadelerinin aksine, görev ifadeleri örtük olarak bir iptal belirteci geçirmez ve iptal denetimlerini örtülü olarak gerçekleştirmez. Kodunuz bir iptal belirteci gerektiriyorsa, iptal belirtecini bir parametre olarak belirtmeniz gerekir. Örnek:
open System.Threading
let someTaskCode (cancellationToken: CancellationToken) =
task {
cancellationToken.ThrowIfCancellationRequested()
printfn $"continuing..."
}
Kodunuzu doğru bir şekilde iptal etmek istiyorsanız iptal belirtecini, iptali destekleyen tüm .NET kitaplığı işlemlerine iletdiğinizi dikkatle kontrol edin. Örneğin, Stream.ReadAsync biri iptal belirtecini kabul eden birden fazla aşırı yükleme içerir. Bu aşırı yüklemeyi kullanmıyorsanız, belirli bir zaman uyumsuz okuma işlemi iptal edilemez.
Arka plan görevleri
Varsayılan olarak, .NET görevleri varsa kullanılarak zamanlanır SynchronizationContext.Current . Bu, görevlerin kullanıcı arabirimi iş parçacığı üzerinde çalışan ve Kullanıcı arabirimi engellemeden yürütülen ve birlikte bulunan, araya eklemeli aracılar olarak görev yapmasına olanak sağlar. Mevcut değilse, görev devamlılıkları .NET iş parçacığı havuzu için zamanlanır.
Uygulamada, genellikle görevleri oluşturan kitaplık kodu eşitleme bağlamını yoksayar ve bunun yerine her zaman, gerekirse .NET iş parçacığı havuzuna geçiş yapar. Bu, kullanılarak sağlanabilir backgroundTask { } :
backgroundTask { expression }
Arka plan görevi SynchronizationContext.Current Şu anlamda yok sayılır: null olmayan bir iş parçacığında başlatılmışsa SynchronizationContext.Current , kullanarak iş parçacığı havuzundaki bir arka plan iş parçacığına geçiş yapar Task.Run . Null içeren bir iş parçacığında başlatılmışsa SynchronizationContext.Current , aynı iş parçacığı üzerinde yürütülür.
Not
Pratikte bu, ' a yapılan çağrıların ConfigureAwait(false) F # görev kodunda genellikle gerekli olmadığı anlamına gelir. Bunun yerine, arka planda çalışması amaçlanan görevlerin kullanılarak yazılması gerekir backgroundTask { ... } . Arka plan görevine yönelik herhangi bir dış görev bağlantısı, SynchronizationContext.Current arka plan görevinin tamamlanmasıyla yeniden eşitlenir.
Uyararamalarıyla ilgili görevlerin sınırlamaları
F # zaman uyumsuz ifadelerinin aksine, görev ifadeleri, bu, bir olarak uyararamadır. Diğer bir deyişle, çalıştırıldığında return! , geçerli görev, sonucu döndürülen görevi bekliyor olarak kaydedilir. Bu, görev ifadeleri kullanılarak uygulanan özyinelemeli işlevlerin ve yöntemlerin sınırsız sayıda görev oluşturmayacağı ve bu, sınırlandırılmamış yığın veya yığın kullanması anlamına gelir. Örneğin, aşağıdaki kodu göz önünde bulundurun:
let rec taskLoopBad (count: int) : Task<string> =
task {
if count = 0 then
return "done!"
else
printfn $"looping..., count = {count}"
return! taskLoopBad (count-1)
}
let t = taskLoopBad 10000000
t.Wait()
Bu kodlama stili, görev ifadeleriyle birlikte kullanılmamalıdır — , bir 10000000 görevi zinciri oluşturacak ve bir hatasına neden olacak StackOverflowException . Her döngü çağrısına zaman uyumsuz bir işlem eklenirse, kod aslında sınırsız bir yığın kullanır. Bu kodu açık bir döngü kullanmak için değiştirmeyi düşünün, örneğin:
let taskLoopGood (count: int) : Task<string> =
task {
for i in count .. 1 do
printfn $"looping... count = {count}"
return "done!"
}
let t = loopBad 10000000
t.Wait()
Zaman uyumsuz olarak yapılan bir çağrı gerekiyorsa, bir F # Async ifadesi kullanın ve bu, uygulanan çağrıları destekler. Örnek:
let rec asyncLoopGood (count: int) =
async {
if count = 0 then
return "done!"
else
printfn $"looping..., count = {count}"
return! asyncLoopGood (count-1)
}
let t = loop 1000000 |> Async.StartAsTask
t.Wait()
Görev uygulama
Görevler, F # 6 ' da yeni bir özellik olan sürdürülebilir Code kullanılarak uygulanır. Görevler, F # derleyicisi tarafından "sürdürülebilir State Machines" olarak derlenir. Bunlar, RFC sürdürülebilir kodundave F # derleyicisi topluluk oturumundaayrıntılı olarak açıklanmaktadır.