Aracılığıyla paylaş


Dış görevler ve tanecikler

Tasarım gereği, grain kodundan oluşturulan tüm alt görevler (örneğin, await, ContinueWith veya Task.Factory.StartNew kullanılarak) üst görevle aynı TaskScheduler başına etkinleştirmede dağıtılır. Bu nedenle, taneli kodun geri kalanıyla aynı tek iş parçacıklı yürütme modelini devralır. Bu, tek iş parçacıklı grenli dönüş tabanlı eşzamanlılık yürütmesinin arkasındaki ana noktadır.

Bazı durumlarda, belirli kodlar Orleans görev zamanlama modelinden "ayrılmalı" ve "özel bir şey yapmalı," örneğin Task üzerinde farklı bir görev zamanlayıcısına veya .NET ThreadPool'ye açıkça işaret edebilir. Örnek olarak, zaman uyumlu bir uzaktan engelleme çağrısı (uzak G/Ç gibi) yürütmesi gereken tanecik kodu gösteriliyor. Bu engelleme çağrısının tanecik bağlamında yürütülmesi, taneciği engeller ve bu nedenle hiçbir zaman yapılmamalıdır. Bunun yerine, grain kodu bu engelleme kodu parçasını bir iş parçacığı havuzunda yürütebilir, bu yürütmenin tamamlanmasına katılabilir (await) ve ardından grain bağlamında ilerleyebilir. Zamanlayıcıdan Orleans kaçışın, tipik kullanım modellerinin ötesinde çok gelişmiş ve nadiren gerekli bir kullanım senaryosu olması beklenir.

Görev tabanlı API'ler

  1. await, TaskFactory.StartNew (aşağıya bakın), Task.ContinueWith, Task.WhenAny, Task.WhenAllve Task.Delay tümü geçerli görev zamanlayıcıya saygı gösterir. Bunları farklı bir TaskScheduler belirtmeden, varsayılan şekilde kullanmak, onların tanecik bağlamında çalışmasına neden olur.

  2. Hem hem de Task.Run temsilcisi endMethod geçerli görev zamanlayıcısını dikkate almazTaskFactory.FromAsync. Her ikisi de .NET iş parçacığı havuzu görev zamanlayıcısını kullanır TaskScheduler.Default . Bu nedenle, Task.Run ve endMethod içindeki kod, Task.Factory.FromAsyncher zaman .NET iş parçacığı havuzunda, tanecikler için Orleans tek iş parçacıklı yürütme modelinin dışında çalışır. Ancak, await Task.Run veya await Task.Factory.FromAsync sonrası tüm kodlar, görevin oluşturulduğu noktada etkin olan zamanlayıcı altında çalışır ve bu zamanlayıcı, görevin zamanlayıcısıdır.

  3. Task.ConfigureAwait ile false , geçerli görev zamanlayıcısından kaçmak için açık bir API'dir. Task'ın ardından kodun TaskScheduler.Default zamanlayıcısında (.NET iş parçacığı havuzu) yürütülmesine neden olur ve böylece taneciğin tek iş parçacıklı yürütülmesini bozar.

    Dikkat

    Genel olarak tahıl kodunda ConfigureAwait(false) doğrudan hiçbir zaman kullanmayın.

  4. İmzalı async void yöntemler taneciklerle kullanılmamalıdır. Bunlar grafik kullanıcı arabirimi olay işleyicileri için tasarlanmıştır. Bir async void yöntem, bir özel durumun kaçmasına izin verirse geçerli işlemi hemen kilitleyebilir ve özel durumu işlemenin hiçbir yolu yoktur. Bu, zaman uyumsuz bir temsilci List<T>.ForEach(async element => ...) temsilcisine dönüştüğünden, Action<T> ve bir async void kabul eden diğer yöntemler için de geçerlidir.

Task.Factory.StartNew ve async temsilciler

C# dilinde görevleri zamanlamak için genellikle Task.Run yerine Task.Factory.StartNew kullanılması önerilir. Task.Factory.StartNew için hızlı bir web araması tehlikeli olduğunu ve Task.Run'nin her zaman tercih edilmesi önerilir. Ancak, tahılın tek iş parçacıklı yürütme modeli içinde kalmak için Task.Factory.StartNew kullanılmalıdır. Peki, doğru şekilde nasıl kullanılır? Task.Factory.StartNew()'nin tehlikesi, zaman uyumsuz temsilciler için yerel destek olmamasıdır. Bu, gibi var notIntendedTask = Task.Factory.StartNew(SomeDelegateAsync) kodun büyük olasılıkla bir hata olduğu anlamına gelir. notIntendedTask, sona erdiğinde tamamlanan bir SomeDelegateAsync. Bunun yerine, döndürülen görevi her zaman açın: var task = Task.Factory.StartNew(SomeDelegateAsync).Unwrap().

Örnek: Birden çok görev ve görev zamanlayıcı

Aşağıda, TaskScheduler.Current, Task.Run ve tahıl bağlamından çıkmak ve ona nasıl geri dönülmesini göstermek için özel bir zamanlayıcının kullanıldığı örnek kod yer almaktadır.

public async Task MyGrainMethod()
{
    // Grab the grain's task scheduler
    var orleansTS = TaskScheduler.Current;
    await Task.Delay(10_000);

    // Current task scheduler did not change, the code after await is still running
    // in the same task scheduler.
    Assert.AreEqual(orleansTS, TaskScheduler.Current);

    Task t1 = Task.Run(() =>
    {
        // This code runs on the thread pool scheduler, not on Orleans task scheduler
        Assert.AreNotEqual(orleansTS, TaskScheduler.Current);
        Assert.AreEqual(TaskScheduler.Default, TaskScheduler.Current);
    });

    await t1;

    // We are back to the Orleans task scheduler.
    // Since await was executed in Orleans task scheduler context, we are now back
    // to that context.
    Assert.AreEqual(orleansTS, TaskScheduler.Current);

    // Example of using Task.Factory.StartNew with a custom scheduler to escape from
    // the Orleans scheduler
    Task t2 = Task.Factory.StartNew(() =>
    {
        // This code runs on the MyCustomSchedulerThatIWroteMyself scheduler, not on
        // the Orleans task scheduler
        Assert.AreNotEqual(orleansTS, TaskScheduler.Current);
        Assert.AreEqual(MyCustomSchedulerThatIWroteMyself, TaskScheduler.Current);
    },
    CancellationToken.None,
    TaskCreationOptions.None,
    scheduler: MyCustomSchedulerThatIWroteMyself);

    await t2;

    // We are back to Orleans task scheduler.
    Assert.AreEqual(orleansTS, TaskScheduler.Current);
}

Örnek: İş parçacığı havuzundaki bir iş parçacığında çalışan koddan bir grain çağrısı yapma

Başka bir senaryoda, tahıla ait görev zamanlama modelinden çıkıp bir iş parçacığı havuzu iş parçacığında (veya bir tane olmayan başka bir bağlamda) çalışması gereken, ancak yine de başka bir tahılı çağırması gereken tahıl kodu yer alır. Tahıl çağrıları, ek tören yapılmadan, tahılsız bağlamlardan yapılabilir.

Aşağıdaki kod, bir grain içinde çalışan ancak grain bağlamında olmayan bir koddan grain çağrısı yapmayı gösterir.

public async Task MyGrainMethod()
{
    // Grab the Orleans task scheduler
    var orleansTS = TaskScheduler.Current;
    var fooGrain = this.GrainFactory.GetGrain<IFooGrain>(0);
    Task<int> t1 = Task.Run(async () =>
    {
        // This code runs on the thread pool scheduler,
        // not on Orleans task scheduler
        Assert.AreNotEqual(orleansTS, TaskScheduler.Current);
        int res = await fooGrain.MakeGrainCall();

        // This code continues on the thread pool scheduler,
        // not on the Orleans task scheduler
        Assert.AreNotEqual(orleansTS, TaskScheduler.Current);
        return res;
    });

    int result = await t1;

    // We are back to the Orleans task scheduler.
    // Since await was executed in the Orleans task scheduler context,
    // we are now back to that context.
    Assert.AreEqual(orleansTS, TaskScheduler.Current);
}

Kitaplıklarla çalışma

Kod tarafından kullanılan bazı harici kütüphaneler dahili olarak ConfigureAwait(false) kullanabilir. .NET'te genel amaçlı kitaplıkları uygularken ConfigureAwait(false) kullanmak iyi ve doğru bir uygulamadır. Bu, Orleans için bir sorun değildir. Kütüphane yöntemini çağıran grain kodu, normal bir await kütüphane çağrısı beklediği sürece doğru olur. Sonuç tam olarak istenen şekilde olur: Kitaplık kodu, varsayılan zamanlayıcıda devamlılıkları çalıştırır (tarafından döndürülen TaskScheduler.Defaultdeğer, genellikle önceki iş parçacığında çizili oldukları için sürekliliklerin bir ThreadPool iş parçacığında çalıştırılmasını garanti etmez), tahıl kodu ise tahılın zamanlayıcısında çalışır.

Sık sorulan bir diğer soru, kitaplık çağrılarının ile Task.Runyürütülmesi gerekip gerekmediğidir; yani kitaplık kodunun ThreadPool 'a açıkça yük boşaltması gerekip gerekmediğidir (örn. await Task.Run(() => myLibrary.FooAsync())). Yanıt hayır. Kodu ThreadPool'a aktarmak, sadece kitaplık kodu zaman uyumlu çağrıları engelleyici şekilde çalışıyorsa gereklidir. Genellikle, iyi yazılmış ve doğru bir .NET zaman uyumsuz kitaplık (konumlarına Task döndüren ve adında Async soneki bulunan yöntemler) engelleyici çağrılar yapmaz. Bu nedenle, zaman uyumsuz kitaplığın hatalı olduğundan şüpheleniliyorsa veya zaman uyumlu bir engelleme kitaplığı bilinçli bir şekilde kullanılıyorsa, herhangi bir şeyin ThreadPool öğesine aktarılması gerekmez.

Kilitlenmeler

Tanecikler tek iş parçacığında çalıştırıldığından, bir taneciği kilitli duruma getirmek mümkündür; bu, birden fazla iş parçacığının engelini kaldırmasını gerektiren bir yöntemle zaman uyumlu bir şekilde engellenerek yapılabilir. Bu, aşağıdaki yöntem ve özelliklerden herhangi birini çağıran kodun, sağlanan görevler yöntemin veya özelliğin çağrılma anına kadar tamamlanmadığında grain nesnesinin kilitlenmesine neden olabileceği anlamına gelir.

  • Task.Wait()
  • Task.Result
  • Task.WaitAny(...)
  • Task.WaitAll(...)
  • task.GetAwaiter().GetResult()

Düşük performansa ve tutarsızlığa yol açabilecekleri için herhangi bir yüksek eşzamanlılık hizmetinde bu yöntemlerden kaçının. .NET'i, yararlı işler gerçekleştirebilecek iş parçacıklarını engelleyerek aç bırakıyorlar ve tamamlanması için ThreadPool'nin ek iş parçacıkları eklemesini gerektiriyorlar. Tahıl kodu yürütülürken, bu yöntemler tahılın kilitlenmesine neden olabilir, bu nedenle bunları tahıl kodu içinde kullanmaktan kaçının.

Bazı senkronizasyon-üstü-asenkron işler kaçınılmazsa, bu işleri ayrı bir zamanlayıcıya taşımak en iyi yoldur. En basit yol, örneğin kullanmaktır await Task.Run(() => task.Wait()). Sync-over-async çalışmalarından kaçınılmasının şiddetle tavsiye edildiğini, çünkü bunun uygulama ölçeklenebilirliğine ve performansına zarar verdiğini unutmayın.

Özet: Orleans'de görevlerle çalışma

Ne yapmaya çalışıyorsunuz? Nasıl yapılır
.NET iş parçacığı havuzu iş parçacıklarında arka plan çalışması çalıştırın. Tahıl kodu veya taneli çağrılara izin verilmez. Task.Run
Sıra tabanlı eşzamanlılık garantileriyle Orleans zaman uyumsuz çalışan görevini tahıl kodundan çalıştırın (yukarıya bakın). Task.Factory.StartNew(WorkerAsync).Unwrap() (Unwrap)
Zaman uyumlu çalışan görevini sıra tabanlı eşzamanlılık garantileriyle Orleans tahıl kodundan çalıştırın. Task.Factory.StartNew(WorkerSync)
İş öğelerini yürütmek için zaman aşımları Task.Delay + Task.WhenAny
Zaman uyumsuz kitaplık yöntemini çağırma await kitaplık çağrısı
async / await komutunu kullanma Normal .NET Görev-Zaman Uyumsuz programlama modeli. Desteklenen ve önerilen
ConfigureAwait(false) Tahıl kodunun içinde kullanmayın. Yalnızca kitaplıkların içinde izin verilir.