Paralel olarak birden fazla zaman uyumsuz servis çağırma


17

Birbirine bağımlı olmayan birkaç zaman uyumsuz REST hizmetim var. Bu Service1 bir yanıt "bekliyor" iken, ben Service2, Service3 ve benzeri arayabilirsiniz.

Örneğin, aşağıdaki koda bakın:

var service1Response = await HttpService1Async();
var service2Response = await HttpService2Async();

// Use service1Response and service2Response

Şimdi, service2Responsebağımlı değildir service1Responseve bağımsız olarak getirilebilirler. Bu nedenle, ikinci hizmeti çağırmak için ilk hizmetin yanıtını beklememe gerek yok.

Parallel.ForEachCPU bağlı çalışma olmadığı için burada kullanabileceğimi sanmıyorum .

Bu iki işlemi paralel olarak çağırmak için kullanımı arayabilir miyim Task.WhenAll? Kullanırken gördüğüm bir sorun Task.WhenAll, sonuç döndürmüyor olmasıdır. Sonucu almak için , tüm görevler zaten tamamlandığından ve bize yanıt almak için ihtiyacım olduğu için task.Resultaradıktan sonra arayabilir Task.WhenAllmiyim?

Basit kod:

var task1 = HttpService1Async();
var task2 = HttpService2Async();

await Task.WhenAll(task1, task2)

var result1 = task1.Result;
var result2 = task2.Result;

// Use result1 and result2

Bu kod performans açısından ilk koddan daha mı iyi? Kullanabileceğim başka bir yaklaşım var mı?


I do not think I can use Parallel.ForEach here since it is not CPU bound operation- Orada mantığı görmüyorum. Eşzamanlılık eşzamanlılıktır.
Robert Harvey

3
@RobertHarvey Sanırım endişe, bu bağlamda, Parallel.ForEachyeni iş parçacıkları ortaya çıkarken, async awaither şeyi tek bir iş parçacığı üzerinde yapacak olmasıdır.
MetaFight

@Ankit, kodunuzun engellenmesi uygun olduğunda buna bağlıdır. Her iki yanıt da hazır olana kadar ikinci örneğiniz engellenir. Muhtemelen ilk örneğiniz, yalnızca kod awaithazır olmadan önce yanıt ( ) kullanmaya çalıştığında mantıksal olarak engellenir .
MetaFight

Her iki hizmet yanıtını tüketen kodun daha az soyut bir örneğini sağladıysanız, size daha tatmin edici bir cevap vermek daha kolay olabilir.
MetaFight

Benim ikinci örnekte @MetaFight yapıyorum WhenAllbenden önce Result.Result çağrılmadan önce tüm görevleri tamamlar fikrine sahip. Task.Result çağıran evreyi engellediğinden, görevler tamamlandıktan sonra çağırırsam sonuçların hemen döneceğini varsayıyorum. Anlayışı doğrulamak istiyorum.
Ankit Vijay

Yanıtlar:


17

Görev kullanarak gördüğüm bir sorun.WhenAll sonuç döndürmüyor

Ama does sonuçları döndürür. Hepsi bu yüzden her zaman değil, ortak bir türde bir dizide olacak kullanışlı size karşılık o dizideki öğeyi bulmalıyız sonuçları kullanmayı Taskiçin sonuç istediğiniz, ve potansiyel için döküm onun gerçek tür, bu nedenle bu bağlamda en kolay / en okunabilir yaklaşım olmayabilir, ancak her görevden tüm sonuçlara sahip olmak istediğinizde ve ortak tür, onlara davranmak istediğiniz türdür, o zaman harika .

Sonucu almak için görev diyebilirim.Task.WhenAll'ı çağırdıktan sonra sonuç, tüm görevler zaten tamamlandığından ve bize yanıt getirmem gereken tek şey mi?

Evet, yapabilirsin. Bunları da yapabilirsiniz await( awaitherhangi bir hatalı görevdeki istisnayı çözer, oysa Resulttoplu bir istisna atar, ancak aksi takdirde aynı olur).

Bu kod performans açısından ilk koddan daha mı iyi?

İki işlemi biri sonra diğeri yerine aynı anda gerçekleştirir. Bunun daha iyi veya daha kötü olup olmadığı, bu temel operasyonların ne olduğuna bağlıdır. Temel işlemler "diskten bir dosya oku" ise, yalnızca bir disk kafası olduğundan ve herhangi bir zamanda yalnızca bir yerde olabileceğinden, paralel olarak yapılması muhtemelen daha yavaştır; iki dosya arasında atlamak bir dosyayı diğerini okumaktan daha yavaş olacaktır. Öte yandan, işlemler "bazı ağ isteği gerçekleştir" ise (burada olduğu gibi), çok daha hızlı olurlar (en azından belirli sayıda eşzamanlı isteğe kadar), çünkü bir yanıt bekleyebilirsiniz başka bir ağ bilgisayarından da beklemede olan başka bir ağ isteği olduğunda o kadar hızlı. Eğer bilmek istiyorsan '

Kullanabileceğim başka bir yaklaşım var mı?

Yaptığınız tüm istisnaları sadece birincisinden ziyade paralel olarak yaptığınız tüm istisnaları bilmeniz sizin için önemli değilse await, görevleri basitçe yapabilirsiniz WhenAll. WhenAllSize verilen tek şey AggregateException, ilk hatalı görevi vurduğunuzda atmak yerine, her hatalı görevdeki her istisna dışında bir sahip olmaktır. Bu kadar basit:

var task1 = HttpService1Async();
var task2 = HttpService2Async();

var result1 = await task1;
var result2 = await task2;

Bu, paralel olarak yalnız başına görevleri yerine getirmiyor. Her görevin sırayla tamamlanmasını bekliyorsunuz. Performans kodu ile ilgilenmiyorsanız tamamen iyi.
Rick O'Shea

3
@ RickO'Shea İşlemleri sırayla başlatır . Bu olacak o * başladıktan sonra ikinci operasyon başlatmak ilk operasyonu. Ancak asenkron işlemin başlatılması temelde anlık olmalıdır (eğer değilse, aslında asenkron değildir ve bu yöntemde bir hatadır). Birini ve sonra diğerini başlattıktan sonra, ilk bitirene kadar ve sonra ikinci bitene kadar devam etmeyecektir. Hiçbir şey, ikinciye başlamadan önce ilkinin bitmesini beklemediğinden, hiçbir şey aynı anda çalışmalarını durduramaz (paralel olarak çalışanlarla aynıdır).
Servy

@Servy Bunun doğru olduğunu düşünmüyorum. Her biri yaklaşık bir saniye süren (her ikisi de http çağrısı yapar) iki asenkron işlemin içine günlük kaydı ekledim ve daha sonra önerdiğiniz gibi onları aradım ve yeterince görev1 başladı ve bitti ve sonra görev2 başladı ve bitti.
Matt Frear

@MattFrear O zaman yöntem aslında asenkron değildi. Eşzamanlıydı. Tanım olarak , işlem gerçekten bittikten sonra dönmek yerine, zaman uyumsuz bir yöntem hemen dönecektir.
Servy

@ Tanımı gereği, beklemek, bir sonraki satırı yürütmeden önce eşzamansız görevin bitmesini beklediğiniz anlamına gelir. Öyle değil mi?
Matt Frear

0

İşte SemaphoreSlim'i kullanan ve maksimum paralellik derecesini ayarlamaya izin veren uzatma yöntemi

    /// <summary>
    /// Concurrently Executes async actions for each item of <see cref="IEnumerable<typeparamref name="T"/>
    /// </summary>
    /// <typeparam name="T">Type of IEnumerable</typeparam>
    /// <param name="enumerable">instance of <see cref="IEnumerable<typeparamref name="T"/>"/></param>
    /// <param name="action">an async <see cref="Action" /> to execute</param>
    /// <param name="maxDegreeOfParallelism">Optional, An integer that represents the maximum degree of parallelism,
    /// Must be grater than 0</param>
    /// <returns>A Task representing an async operation</returns>
    /// <exception cref="ArgumentOutOfRangeException">If the maxActionsToRunInParallel is less than 1</exception>
    public static async Task ForEachAsyncConcurrent<T>(
        this IEnumerable<T> enumerable,
        Func<T, Task> action,
        int? maxDegreeOfParallelism = null)
    {
        if (maxDegreeOfParallelism.HasValue)
        {
            using (var semaphoreSlim = new SemaphoreSlim(
                maxDegreeOfParallelism.Value, maxDegreeOfParallelism.Value))
            {
                var tasksWithThrottler = new List<Task>();

                foreach (var item in enumerable)
                {
                    // Increment the number of currently running tasks and wait if they are more than limit.
                    await semaphoreSlim.WaitAsync();

                    tasksWithThrottler.Add(Task.Run(async () =>
                    {
                        await action(item).ContinueWith(res =>
                        {
                            // action is completed, so decrement the number of currently running tasks
                            semaphoreSlim.Release();
                        });
                    }));
                }

                // Wait for all tasks to complete.
                await Task.WhenAll(tasksWithThrottler.ToArray());
            }
        }
        else
        {
            await Task.WhenAll(enumerable.Select(item => action(item)));
        }
    }

Örnek Kullanımı:

await enumerable.ForEachAsyncConcurrent(
    async item =>
    {
        await SomeAsyncMethod(item);
    },
    5);

-2

Ya kullanabilirsiniz

Parallel.Invoke(() =>
{
    HttpService1Async();
},
() =>
{   
    HttpService2Async();
});

veya

Task task1 = Task.Run(() => HttpService1Async());
Task task2 = Task.Run(() => HttpService2Async());

//If you wish, you can wait for a particular task to return here like this:
task1.Wait();

Neden vahşiler?
user1451111
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.