2013-07-07 15 views
5

Böyle bazı yavaş çalışır bir görev oluşturur bazı kodlar vardır:Bekleme çağrıldığında, eşzamanlı sürümle aynı davranan gövde içinde bekleyen bir Görev nasıl oluşturulur?

public static Task wait1() 
{ 
    return new Task(() => 
    { 
     Console.WriteLine("Waiting..."); 
     Thread.Sleep(10000); 
     Console.WriteLine("Done!"); 
    }); 
} 

gerçek uygulamada, Thread.Sleep aslında bir web hizmeti çağrı olur. Yöntemin gövdesini beklemek için kullanabilirim (böylece ağ erişimi/uyku sırasında bir iplik tüketmez). Benim ilk girişimi bu oldu (av tüfeği-ayıklama derleme hataları dayanarak):

public static Task wait2() 
{ 
    return new Task(async() => 
    { 
     Console.WriteLine("Waiting..."); 
     await Task.Delay(10000); 
     Console.WriteLine("Done!"); 
    }); 
} 

Ancak; Bu görev ilkininkiyle aynı gibi görünmüyor, çünkü .Wait() 'i çağırdığımda; hemen döner.

Aşağıda, farklılıkları gösteren tam bir örnek (konsol uygulaması) bulunmaktadır (uygulama, ikinci görev başladığında hemen sona erecektir).

İçinde beklemede olan bir kodun bulunması gereken bir Görevde Bekle ve Bekle'yi çağırabilmem için ne yapmam gerekiyor? Görevler daha sonra bir aracı tarafından sıraya konur ve yürütülür, bu nedenle görevin otomatik olarak başlatılmaması önemlidir.

class Program 
{ 
    static void Main(string[] args) 
    { 
     var w1 = wait1(); 
     w1.Start(); 
     w1.Wait(); // This waits 110 seconds 

     var w2 = wait2(); 
     w2.Start(); 
     w2.Wait(); // This returns immediately 
    } 

    public static Task wait1() 
    { 
     return new Task(() => 
     { 
      Console.WriteLine("Waiting..."); 
      Thread.Sleep(10000); 
      Console.WriteLine("Done!"); 
     }); 
    } 

    public static Task wait2() 
    { 
     return new Task(async() => 
     { 
      Console.WriteLine("Waiting..."); 
      await Task.Delay(10000); 
      Console.WriteLine("Done!"); 
     }); 
    } 
} 
+2

Neden yeni bir görev yapalım? Neden dış yöntemi 'eşzamansız 'yapmaz ve' Görev 'i otomatik olarak döndürür? Yani, statik async Görev bekle3() {Console.WriteLine ("..."); Task.Delay (10000) bekliyor; Console.WriteLine ("..."); } ' –

+0

@EricLippert Bunu denedim; ama farklı davranıyor gibi görünüyor ... 'Calling' .Start() 'System.InvalidOperationException atar: Start, söz stili tarzında bir görev çağrılmayabilir. Görevlerim daha sonra çağrılacak bir sıraya giriyor; bu yüzden onları daha sonra başlatabilirim. –

+0

Amacınız, Start çağrılıncaya kadar beklemekte beklemekte beklemekte? Bunu, "Daha sonra Başlat'ı arayabilmem gerekiyor." Eğer durum buysa, sanırım soru, beklediğiniz yürütme ile güncellenmelidir. –

cevap

8

Bu mümkün değil gibi görünüyor! alexm's answer here bakınız: zaman uyumsuz yöntemlerle döndü

Görevler her zaman onlar devlet Running içinde sıcak oluşturulur yani. Ajanımı yaparak bu sorunu çalıştık

:-(yerine Func<Task>s sıraya, ve bir görev aldığında aşırı yük basitçe () => task sıralar.Sonra; de-queing zaman bir görevi Çalışıyorsa değilse, ben kontrol ve eğer öyleyse, bunu başlatmak: Bu basit bir çözüm çalışırsa

var currentTask = currentTaskFunction(); 
if (currentTask.Status == TaskStatus.Created) 
    currentTask.Start(); 

O (Bunu yapmak zorunda kalmak biraz aksak görünüyor; neden orijinal kısıtlama async yöntemleri her zaman sıcak oluşturuluyor?), ama benim için işe yaradığı göründüğü gibi :-)

+2

Aslında, tam olarak yapmak istediğiniz şey budur. Async dünyasında, bir 'Görev' zaten çalışıyor, bu yüzden eğer * bir görevde Daha sonra kullanmak için doğru tür 'Func ' 'dır. –

+0

@StephenCleary Sadece bunu blog yazısı buldum ve neredeyse yazdığımın tam bir kopyasıdır! http://blog.stephencleary.com/ 2012/12/return-erken-from-aspnet-requests.html Aracım her seferinde bir sıraya işleyen bir döngüde otursa da! –

+0

Eşzamansız yöntemlerin döndürülen görevleri döndürdüğünün kafa karıştırıcı olacağını görebiliyorum. 't başladı, sadece dela içinde nasıl kullanılacağını açık değil yapar bir utanç var Görevler –

2

Sen olarak bu yazabiliriz: Genel olarak

public static async Task Wait2() 
{ 
    Console.WriteLine("Waiting..."); 
    await Task.Delay(10000); 
    Console.WriteLine("Done!"); 
} 

, nadiren hiç new Task veya new Task<T> kullanmak iyi bir fikirdir. Oluşturmak için async/await dil desteğini kullanmak yerine ThreadPool'u kullanarak bir görevi başlatmanız gerekiyorsa, görevi başlatmak için Task.Run kullanmalısınız. Bu, çalıştırılacak görevi zamanlayacaktır (önemli olan, görevlerin her zaman "sıcaklar" şeklinde olması gerekir).

Bunu yapmanın, Task.Start numaralı telefonu aramak zorunda kalmayacağını unutmayın.

+0

Kasıtlı olarak Start'ı arıyorum. Uygulamamda, görevler "sıraya alındı" ve daha sonra işleniyor.Hemen hemen her görev kuyruğunda ortada bir web servis çağrısı olacağı için iplikleri boşaltmak için beklemek kullanmak istiyorum; beklemek güzel olurdu :-) –

0

bu deneyin:

public async static Task wait2() 
{ 
     Console.WriteLine("Waiting..."); 
     await Task.Delay(2000); 
     Console.WriteLine("Done!"); 
} 

Ama biz farkında görev zaten bu yüzden başlatıldığını aramak gerekmez başlatın:

var w2 = wait2(); 
//w2.Start(); 
w2.Wait(); 

Ben senin wait2 işlevi ile sorun olduğunu düşünüyorum 2 görev, new Task(...)'dan ve Task.Delay()'dan başka bir tane yaratıyor. İlkini bekliyorsun, ama iç olanı beklemiyorsun.

+0

Daha sonra 'Start' diyebilmek için buna ihtiyacım var, bu işe yaramayacaktır :(Birden fazla görevi yerine getirme konusunda haklı olabileceğinden şüpheleniyorum, ama bu bana dışarıdan gelen işin kafamı karıştırıyor gibi görünüyor. işin tüm bloğunu kapsamaz :( –

1

Bu async/beklemenin aslında yeni bir iş parçacığı oluşturmayacağını değil, kodun o kısmını planladığını anlamanıza yardımcı olmak için zamanında mevcut bir noktada koştu.

Yeni Görev (async() => ...) oluşturduğunuzda, bir async yöntemi çalıştıran bir göreviniz vardır. Bu içsel uyumsuzluk yöntemine ulaşıldığında 'yeni görev' tamamlanmış sayılır, çünkü geri kalanı programlanmıştır. Bekleme komutundan önce 'yeni Görev' içinde bazı kodları (istenirse çok) daha iyi anlamanıza yardımcı olmak için. Başvurunun sona ermesinden önce her şey yerine getirilecek ve bir kez beklendiğinde bu görevin tamamlandığını düşünecektir. Daha sonra uygulamayı döndürür ve çıkar.

Bundan kaçınmanın en iyi yolu, görevinizin içine herhangi bir görev veya uyumsuzluk yöntemi yerleştirmemenizdir.

Async anahtar sözcüğünü ve anahtar sözcüğünü yöntemden kaldırın ve beklendiği gibi çalışır.

Bu, alışkınsanız bir geri arama oluşturmakla aynı şeydir.

void MethodAsync(Action callback) 
{ 
    //...some code 
    callback?.Invoke(); 
} 

//using this looks like this. 
MethodAsync(() => { /*code to run when complete */}); 

Bu temelde bir görev içerisinde yeni bir görev oluştururken olmasıdır

Task MethodAsync() 
{ 
    //... some code here 
} 

//using it 

await MethodAsync(); 
/*code to run when complete */ 

anlamak şey aynıdır //. Dolayısıyla, iç geri '' geri arama '' anahtar sözcük bekliyor.

Sen kod şöyle ..

void MethodAsync(Action callback) 
{ 
    //some code to run 
    callback?.Invoke(); // <- this is the await keyword 
    //more code to run.. which happens after we run whoever is 
    //waiting on callback 
} 

açıkçası eksik kod var. Bu mantıklı değilse lütfen bizimle iletişime geçmekten çekinmeyin ve yardımcı olacağım. Async/await (işleri daha basit hale getirmek için), başınızı ilk önce etrafa saracak bir yaratıktır. Daha sonra onu alırsınız, o zaman muhtemelen en sevdiğiniz şeydir. : P