İki eşzamansız görevi paralel olarak çalıştırın ve sonuçları .NET 4.5'te toplayın


116

Bir süredir .NET 4.5 ile çalışmanın basit olacağını düşündüğüm bir şeyi elde etmeye çalışıyorum.

İki uzun süreli görevi aynı anda başlatmak ve
sonuçları en iyi C # 4.5 (RTM) yöntemiyle toplamak istiyorum.

Aşağıdakiler çalışıyor ama hoşuma gitmiyor çünkü:

  • Zaman Sleepuyumsuz bir yöntem olmak istiyorum , böylece awaitdiğer yöntemler olabilir
  • Sadece beceriksiz görünüyor Task.Run()
  • Bunun herhangi bir yeni dil özelliğini kullandığını bile düşünmüyorum!

Çalışma kodu:

public static void Go()
{
    Console.WriteLine("Starting");

    var task1 = Task.Run(() => Sleep(5000));    
    var task2 = Task.Run(() => Sleep(3000));

    int totalSlept = task1.Result + task2.Result;

    Console.WriteLine("Slept for a total of " + totalSlept + " ms");
}

private static int Sleep(int ms)
{
    Console.WriteLine("Sleeping for " + ms);
    Thread.Sleep(ms);
    Console.WriteLine("Sleeping for " + ms + " FINISHED");
    return ms;
}

Çalışmayan kod:

Güncelleme: Bu aslında işe yarıyor ve bunu yapmanın doğru yolu, tek sorun Thread.Sleep

Bu kod çalışmaz çünkü Sleep(5000)görevi hemen çalıştırma çağrısı Sleep(1000), tamamlanana kadar çalışmaz. Bu doğru olsa Sleepda asyncve çok erken kullanmıyorum awaitveya aramıyorum .Result.

Belki de Task<T>bir asyncyöntemi çağırarak çalışmayan bir çalışma yapmanın bir yolu olduğunu düşündüm, böylece daha sonra Start()iki görevi arayabilirim , ancak zaman Task<T>uyumsuz bir yöntemi çağırmaktan nasıl bir sonuç alacağımı bulamıyorum.

public static void Go()
{
    Console.WriteLine("Starting");

    var task1 = Sleep(5000);    // blocks
    var task2 = Sleep(1000);

    int totalSlept = task1.Result + task2.Result;

    Console.WriteLine("Slept for " + totalSlept + " ms");
}

private static async Task<int> Sleep(int ms)
{
    Console.WriteLine("Sleeping for " + ms);
    Thread.Sleep(ms);
    return ms;
}

not: Go'yu eşzamansız bir yöntem yapmak fark etmez
Simon_Weaver

3
Blok de oluyor task1.Resultdeğil var task1 = Sleep(5000)bir bekliyoruz anahtar kelime olmadan Uyku yöntemi senkron çünkü.
Arvis

Yanıtlar:


86

Zaman uyumsuz programlama için Uyku yerine Task.Delay kullanmalı ve ardından görev sonuçlarını birleştirmek için Task.WhenAll kullanmalısınız. Görevler paralel olarak yürütülecekti.

public class Program
    {
        static void Main(string[] args)
        {
            Go();
        }
        public static void Go()
        {
            GoAsync();
            Console.ReadLine();
        }
        public static async void GoAsync()
        {

            Console.WriteLine("Starting");

            var task1 = Sleep(5000);
            var task2 = Sleep(3000);

            int[] result = await Task.WhenAll(task1, task2);

            Console.WriteLine("Slept for a total of " + result.Sum() + " ms");

        }

        private async static Task<int> Sleep(int ms)
        {
            Console.WriteLine("Sleeping for {0} at {1}", ms, Environment.TickCount);
            await Task.Delay(ms);
            Console.WriteLine("Sleeping for {0} finished at {1}", ms, Environment.TickCount);
            return ms;
        }
    }

11
Bu harika bir cevap ... ama araştırana kadar bu yanlışın cevap olduğunu düşündüm. sonra anladım. Gerçekten 5 saniyede çalışıyor. İşin püf noktası görevleri hemen BEKLEMEMEK, bunun yerine Task.WhenAll'da beklemektir.
Tim Lovell-Smith

114
async Task<int> LongTask1() { 
  ...
  return 0; 
}

async Task<int> LongTask2() { 
  ...
  return 1; 
}

...
{
   Task<int> t1 = LongTask1();
   Task<int> t2 = LongTask2();
   await Task.WhenAll(t1,t2);
   //now we have t1.Result and t2.Result
}

2
+ 1'im çünkü t1, t2'yi Görev olarak ilan ediyorsunuz, bu doğru yoldur.
Minime

12
Bu çözümün Go yönteminin de eşzamansız olmasını gerektirdiğine inanıyorum, yani eşzamansız olma özelliğini açığa çıkarıyor. Arayanın Goyönteminin eşzamanlı olduğu, ancak iki bağımsız görevi eşzamansız olarak tamamlamak isteyen (yani her ikisinin de diğerinden önce tamamlanması gerekmediği, ancak yürütmeler devam etmeden önce her ikisinin de tamamlanması gereken) soranlara benzer bir şey istiyorsanız , o Task.WaitAllzaman daha iyi olur ve yapmazsınız. await anahtar sözcüğüne gerek yoktur, bu nedenle çağırma Goyönteminin eşzamansız olması için çağırma yöntemine gerek yoktur . Her iki yaklaşım da daha iyi değil, sadece amacınızın ne olduğu meselesi.
AaronLS

1
Void yöntemi: async void LongTask1() {...}Task.Result özelliği yoktur. Böyle durumda T olmadan Görevi kullanın: async Task LongTask1().
Arvis

Her iki görevden de sonuçları alamadım. Ben de olarak değiştirdim Task<TResult> t1 = LongTask1();ve şimdi anlıyorum t1.Result. <TResult>sonucunuzun geri dönen türüdür. Bunun return <TResult>çalışması için yönteminizde a'ya ihtiyacınız olacak.
gilu

1
Gerçekten basit şeyler yapıyorsanız ve fazladan t1ve t2değişkenleri istemiyorsanız kullanabileceğinizi belirtmekte fayda var new Task(...). Örneğin: int int1 = 0; await Task.WhenAll(new Task(async () => { int1 = await LongTask1(); }));. Bu yaklaşımın bir püf noktası, derleyicinin değişkenin atandığını fark etmemesi ve eğer ona bir başlangıç ​​değeri vermezseniz atanmamış olarak davranmasıdır.
Robert Dennis

3

SleepYönteminiz eşzamansız iken Thread.Sleep, değildir. Zaman uyumsuzluğun tüm fikri, birden çok iş parçacığı başlatmak değil, tek bir iş parçacığını yeniden kullanmaktır. Thread.Sleep'e eşzamanlı bir arama yapmayı engellediğiniz için, işe yaramayacak.

Bunun Thread.Sleepgerçekte yapmak istediğiniz şeyin basitleştirilmesi olduğunu varsayıyorum . Gerçek uygulamanız eşzamansız yöntemler olarak kodlanabilir mi?

Birden fazla eşzamanlı engelleme çağrısı çalıştırmanız gerekiyorsa, düşündüğüm başka bir yere bakın!


teşekkürler Richard - evet gerçekten servis
çağrımı

o zaman zaman uyumsuz nasıl çalıştırılır? Çok fazla dosya değiştirme ve dosya için bekleyen uygulama var, yaklaşık 5 saniye ve sonra başka bir işlem, ilk önce "ne zaman" ilk önce çalıştırdığımda, sonra ikinci olarak şunu var x = y()söylememe rağmen: ve henüz değil var x=await y()veya y().wait()hala sonuna kadar bekleyin ve eğer asenkron bunu kendi başına halledemezse ne yapmalıyım? Y'nin eşzamansız ile süslendiğine dikkat edin ve bunun atandığı yerde değil, her şeyin içinde olmasını bekliyorum, DÜZENLEME: tam ne zaman ortağım dedi ki, deneyelim Task.Factoryve kırdığımda işe yaradığını söyledi bu sınıfın tarafı
deadManN

2

Bu noktayı cevaplamak için:

Uykunun eşzamansız bir yöntem olmasını istiyorum, böylece diğer yöntemleri bekleyebilir

Sleepişlevi şu şekilde yeniden yazabilirsiniz :

private static async Task<int> Sleep(int ms)
{
    Console.WriteLine("Sleeping for " + ms);
    var task = Task.Run(() => Thread.Sleep(ms));
    await task;
    Console.WriteLine("Sleeping for " + ms + "END");
    return ms;
}

static void Main(string[] args)
{
    Console.WriteLine("Starting");

    var task1 = Sleep(2000);
    var task2 = Sleep(1000);

    int totalSlept = task1.Result +task2.Result;

    Console.WriteLine("Slept for " + totalSlept + " ms");
    Console.ReadKey();
}

bu kodu çalıştırdığınızda çıktı:

Starting
Sleeping for 2000
Sleeping for 1000
*(one second later)*
Sleeping for 1000END
*(one second later)*
Sleeping for 2000END
Slept for 3000 ms

2

Bu var haftasonu artık!

    public async void Go()
    {
        Console.WriteLine("Start fosterage...");

        var t1 = Sleep(5000, "Kevin");
        var t2 = Sleep(3000, "Jerry");
        var result = await Task.WhenAll(t1, t2);

        Console.WriteLine($"My precious spare time last for only {result.Max()}ms");
        Console.WriteLine("Press any key and take same beer...");
        Console.ReadKey();
    }

    private static async Task<int> Sleep(int ms, string name)
    {
            Console.WriteLine($"{name} going to sleep for {ms}ms :)");
            await Task.Delay(ms);
            Console.WriteLine("${name} waked up after {ms}ms :(";
            return ms;
    }

0

Bu makale birçok şeyi açıklamaya yardımcı oldu. SSS tarzında.

Zaman uyumsuz / Beklemede SSS

Bu bölüm, neden Thread.Sleepaynı orijinal iş parçacığı üzerinde çalıştığını açıklıyor - bu da benim ilk kafa karışıklığıma neden oluyor.

"Async" anahtar sözcüğü, bir yöntemin çağrılmasının ThreadPool'da sıraya girmesine neden olur mu? Yeni bir konu oluşturmak için? Mars'a bir roket gemisi fırlatmak için mi?

Hayır. Hayır. Ve hayır. Önceki sorulara bakın. "Async" anahtar sözcüğü derleyiciye, yöntemin bir bekleme noktasında askıya alınabileceği ve beklenen örnek tamamlandığında eşzamansız olarak yürütülmesine devam edebileceği şekilde, yöntemin içinde "await" kullanılabileceğini belirtir. Bu nedenle derleyici, "zaman uyumsuz" olarak işaretlenmiş bir yöntemin içinde "bekleme" yoksa bir uyarı verir.

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.