Neden senkronize etmek yerine zaman uyumsuz WebAPI işlemleri oluşturmalıyım?


109

Oluşturduğum bir Web API'de aşağıdaki işlemi yapıyorum:

// GET api/<controller>
[HttpGet]
[Route("pharmacies/{pharmacyId}/page/{page}/{filter?}")]
public CartTotalsDTO GetProductsWithHistory(Guid pharmacyId, int page, string filter = null ,[FromUri] bool refresh = false)
{
    return delegateHelper.GetProductsWithHistory(CustomerContext.Current.GetContactById(pharmacyId), refresh);
}

Bu web hizmetine yapılan çağrı bir Jquery Ajax çağrısı aracılığıyla şu şekilde yapılır:

$.ajax({
      url: "/api/products/pharmacies/<%# Farmacia.PrimaryKeyId.Value.ToString() %>/page/" + vm.currentPage() + "/" + filter,
      type: "GET",
      dataType: "json",
      success: function (result) {
          vm.items([]);
          var data = result.Products;
          vm.totalUnits(result.TotalUnits);
      }          
  });

Önceki işlemi bu şekilde uygulayan bazı geliştiriciler gördüm:

// GET api/<controller>
[HttpGet]
[Route("pharmacies/{pharmacyId}/page/{page}/{filter?}")]
public async Task<CartTotalsDTO> GetProductsWithHistory(Guid pharmacyId, int page, string filter = null ,[FromUri] bool refresh = false)
{
    return await Task.Factory.StartNew(() => delegateHelper.GetProductsWithHistory(CustomerContext.Current.GetContactById(pharmacyId), refresh));
}

GetProductsWithHistory () işleminin oldukça uzun bir işlem olduğunu söylemeliyim. Sorunum ve bağlamım göz önüne alındığında, webAPI işleminin eşzamansız hale getirilmesi bana nasıl fayda sağlar?


1
İstemci tarafı, zaten eşzamansız olan AJAX'ı kullanır. Hizmetin bir async Task<T>. Unutmayın, AJAX, TPL olmadan önce uygulandı :)
Dominic Zukiewicz

65
Neden zaman uyumsuz denetleyicileri uyguladığınızı anlamanız gerekir, çoğu da yapmaz. IIS'de sınırlı sayıda iş parçacığı vardır ve tümü kullanımdayken sunucu yeni istekleri işleyemez. Zaman uyumsuz denetleyicilerle, bir işlem G / Ç'nin tamamlanmasını beklerken, iş parçacığı, sunucunun diğer istekleri işlemek için kullanması için serbest bırakılır.
Matija Grcic

3
Hangi geliştiricilerin bunu yaptığını gördün? Bu tekniği öneren herhangi bir blog yazısı veya makale varsa, lütfen bir bağlantı gönderin.
Stephen Cleary

3
Süreciniz yukarıdan (web uygulamasının kendisi ve denetleyicileriniz dahil) işleminizin dışına çıkan beklenebilir etkinliklere (zamanlayıcı gecikmeleri, dosya G / Ç, DB erişimi dahil) kadar eşzamansız farkındalığa sahipse, yalnızca zaman uyumsuzluğundan tam olarak yararlanabilirsiniz. ve yaptığı web istekleri). Bu durumda, temsilci yardımcınızın bir GetProductsWithHistoryAsync()geri dönüşe ihtiyacı vardır Task<CartTotalsDTO>. Denetleyicinizin yaptığı çağrıları da eşzamansız olarak geçirmeyi düşünüyorsanız, denetleyicinizi eşzamansız yazmanın bir yararı olabilir; geri kalanını geçirirken eşzamansız parçalardan yararlanmaya başlarsınız.
Keith Robertson

1
Yaptığınız işlem kapanıyorsa ve veritabanına vuruyorsa, web iş parçacığınız geri dönmesini ve bu iş parçacığını tutmasını bekliyor demektir. Maksimum iş parçacığı sayınıza ulaştıysanız ve başka bir istek gelirse, beklemesi gerekir. Neden bunu yapıyorsun? Bunun yerine, bu iş parçacığını denetleyicinizden serbest bırakmak istersiniz, böylece başka bir istek onu kullanabilir ve yalnızca veritabanından orijinal isteğiniz geri geldiğinde başka bir web dizisini alabilir. msdn.microsoft.com/en-us/magazine/dn802603.aspx
user441521

Yanıtlar:


98

Spesifik örneğinizde, işlem hiç eşzamansız değildir, bu nedenle yaptığınız şey eşzamansızdır. Sadece bir ileti dizisini serbest bırakıp diğerini engelliyorsunuz. Bunun için bir neden yok, çünkü tüm iş parçacıkları iş parçacığı havuzu iş parçacıklarıdır (bir GUI uygulamasının aksine).

"Eşzamansız eşzamanlı olmayan" tartışmamda, dahili olarak eşzamanlı olarak uygulanan bir API'niz varsa, eşzamanlı yöntemi basitçe saran eşzamansız bir eşdüzey göstermemenizi şiddetle tavsiye ettim Task.Run.

Gönderen Zaman uyumsuz yöntemler için eşzamanlı sarmalayıcıları göstermeli miyim?

Bununla birlikte async, oturan ve bir sonuç için bekleyen bir iş parçacığını engellemek yerine gerçek bir eşzamansız işlemin (genellikle G / Ç) olduğu WebAPI çağrıları yaparken iş parçacığı iş parçacığı havuzuna geri döner ve böylece başka bir işlem gerçekleştirebilir. Her şeyden öte bu, uygulamanızın daha az kaynakla daha fazlasını yapabileceği ve bu da ölçeklenebilirliği artıracağı anlamına gelir.


3
@efaruk tüm konular işçi konularıdır. Bir ThreadPool ipliğini serbest bırakmak ve diğerini bloke etmek anlamsızdır.
i3arnon

1
@efaruk Ne demeye çalıştığından emin değilim .. ama kabul ettiğin sürece WebAPI'de eşzamanlı olmayan eşzamanlı kullanmak için bir neden yok, o zaman sorun değil.
i3arnon

@efaruk "async over sync" (ie await Task.Run(() => CPUIntensive())) asp.net'te işe yaramaz. Bunu yapmaktan hiçbir şey kazanmazsın. Bir diğerini işgal etmek için bir ThreadPool iş parçacığı bırakıyorsunuz. Senkronize yöntemi çağırmaktan daha az etkilidir.
i3arnon

1
@efaruk Hayır, bu makul değil. Örneğiniz bağımsız görevleri sırayla çalıştırır. Tavsiyelerde bulunmadan önce gerçekten asyc / beklemeniz gerekiyor. await Task.WhenAllParalel olarak yürütmek için kullanmanız gerekir .
Søren Boisen

1
@efaruk Boisen'in açıkladığı gibi, örneğiniz bu eşzamanlı yöntemleri sıralı olarak çağırmanın üstüne herhangi bir değer katmıyor. Task.RunYükünüzü birden çok iş parçacığı üzerinde paralelleştirmek istiyorsanız kullanabilirsiniz , ancak "eşzamanlı olmayan eşzamanlı" bu anlama gelmez. Eşzamanlı olmayan bir sarmalayıcı olarak zaman uyumsuz bir yöntem oluşturan "eşzamansız" referanslar. Cevabımdaki alıntıda görebilirsiniz.
i3arnon

1

Bir yaklaşım (bunu müşteri uygulamalarında başarılı bir şekilde kullandım) uzun işlemleri çalışan iş parçacıklarıyla çalıştıran bir Windows Hizmetine sahip olmak ve ardından engelleme işlemi tamamlanıncaya kadar iş parçacıklarını serbest bırakmak için bunu IIS'de yapmak olabilir: Not, bu varsayılır sonuçlar bir tabloda (jobId ile tanımlanan satırlar) ve kullanımdan birkaç saat sonra bunları temizleyen daha temiz bir işlemde saklanır.

"Sorunum ve bağlamım göz önüne alındığında, webAPI işleminin eşzamansız hale getirilmesi bana nasıl fayda sağlar?" "oldukça uzun bir işlem" olduğu düşünüldüğünde, ms yerine birkaç saniye düşünüyorum, bu yaklaşım IIS iş parçacıklarını serbest bırakıyor. Açıkçası, kendisi de kaynak alan bir Windows hizmeti çalıştırmanız gerekir, ancak bu yaklaşım, yavaş sorgulardan oluşan bir selin sistemin diğer bölümlerinden iş parçacığı çalmasını önleyebilir.

// GET api/<controller>
[HttpGet]
[Route("pharmacies/{pharmacyId}/page/{page}/{filter?}")]
public async Task<CartTotalsDTO> GetProductsWithHistory(Guid pharmacyId, int page, string filter = null ,[FromUri] bool refresh = false)
{
        var jobID = Guid.NewGuid().ToString()
        var job = new Job
        {
            Id = jobId,
            jobType = "GetProductsWithHistory",
            pharmacyId = pharmacyId,
            page = page,
            filter = filter,
            Created = DateTime.UtcNow,
            Started = null,
            Finished = null,
            User =  {{extract user id in the normal way}}
        };
        jobService.CreateJob(job);

        var timeout = 10*60*1000; //10 minutes
        Stopwatch sw = new Stopwatch();
        sw.Start();
        bool responseReceived = false;
        do
        {
            //wait for the windows service to process the job and build the results in the results table
            if (jobService.GetJob(jobId).Finished == null)
            {
                if (sw.ElapsedMilliseconds > timeout ) throw new TimeoutException();
                await Task.Delay(2000);
            }
            else
            {
                responseReceived = true;
            }
        } while (responseReceived == false);

    //this fetches the results from the temporary results table
    return jobService.GetProductsWithHistory(jobId);
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.