2013-09-03 24 views
17

Temelde bir veritabanına tekrar tekrar konuşmaya ve başlatmaya çalışan bazı eşzamansız kod yazmam gerekiyor. Çoğunlukla, ilk denemede, tekrar denenmesi gerekliliği başarısız olur. benim sorum gerçekten bu en iyisi bu yüzden zaman uyumsuz desen açısından .NET için son zamanlarda yapılmış olan bir çok değişiklik farkEn iyi zaman uyumsuz yöntem

void WaitForItToWork() 
{ 
    bool succeeded = false; 
    while (!succeeded) 
    { 
     // do work 
     succeeded = outcome; // if it worked, mark as succeeded, else retry 
     Threading.Thread.Sleep(1000); // arbitrary sleep 
    } 
} 

: Geçmiş günlerde

Ben benzer bir desen kullanılmış olurdu yöntemi kullanmak veya async öğelerini keşfederken değer ve eğer öyleyse bu modeli async içinde nasıl uygularım?

Güncelleme

bunu çoğaltılır yöntem bitmesi için bir hizmetin yapıcı içinde yaratılacak olacak şekilde beklemek zorunda kalmaması Tıpkı uyumsuz bu işi yumurtlamaya istiyorum netleştirmek için o kadar kurucu anında geri dönmelidir.

cevap

25

Böyle olduğu parçasını planı ayrı olabilir:

async Task<bool> WaitForItToWork() 
{ 
    bool succeeded = false; 
    while (!succeeded) 
    { 
     // do work 
     succeeded = outcome; // if it worked, make as succeeded, else retry 
     await Task.Delay(1000); // arbitrary delay 
    } 
    return succeeded; 
} 

Anlaşılan, bu iş parçacığı havuzu daha verimli kullanılmasıdır verecek tek yararı, her zaman gecikmesini yapmak için bütün bir iplik almaz çünkü olmak.

outcome'u nasıl elde edeceğinize bağlı olarak, bu işi async/await kullanarak gerçekleştirmenin çok daha verimli yolları olabilir. Genellikle GetOutcomeAsync() gibi bir web servisini, veritabanını veya soket çağrısını doğal bir şekilde eşzamanlı olmayan bir şekilde yapabilirsiniz, böylece var outcome = await GetOutcomeAsync()'u yaparsınız.

WaitForItToWork'un derleyici tarafından parçalara ayrılacağı ve await satırının eşzamansız olarak devam edeceği dikkate alınmalıdır. Here's belki de dahili olarak nasıl yapıldığına dair en iyi açıklama. Şey, genellikle kodunuzun bir noktasında, async görevinin sonucu üzerinde senkronize etmeniz gerekecek. Örn .:

private void Form1_Load(object sender, EventArgs e) 
{ 
    Task<bool> task = WaitForItToWork(); 
    task.ContinueWith(_ => { 
     MessageBox.Show("WaitForItToWork done:" + task.Result.toString()); // true or false 
    }, TaskScheduler.FromCurrentSynchronizationContext()); 
} 

Sadece bu yapmış olabilir:

private async void Form1_Load(object sender, EventArgs e) 
{ 
    bool result = await WaitForItToWork(); 
    MessageBox.Show("WaitForItToWork done:" + result.toString()); // true or false 
} 

ancak çok Form1_Load bir zaman uyumsuz yöntem yapmak olacağını.

[GÜNCELLEME]

Aşağıda async/await aslında bu durumda ne yaptığını göstermek için benim girişimidir. Aynı mantıksal iki sürümü oluşturdu, WaitForItToWorkAsync (async/await kullanarak) ve WaitForItToWorkAsyncTap (TAP pattern kullanarak async/await). İkinci versiyonun aksine, frist versiyonu oldukça önemsizdir. Böylece, async/await büyük ölçüde derleyicinin sözdizimsel şekeri iken, asenkron kodları yazmayı ve anlamayı daha kolay hale getirir.

// fake outcome() method for testing 
bool outcome() { return new Random().Next(0, 99) > 50; } 

// with async/await 
async Task<bool> WaitForItToWorkAsync() 
{ 
    var succeeded = false; 
    while (!succeeded) 
    { 
     succeeded = outcome(); // if it worked, make as succeeded, else retry 
     await Task.Delay(1000); 
    } 
    return succeeded; 
} 

// without async/await 
Task<bool> WaitForItToWorkAsyncTap() 
{ 
    var context = TaskScheduler.FromCurrentSynchronizationContext(); 
    var tcs = new TaskCompletionSource<bool>(); 
    var succeeded = false; 
    Action closure = null; 

    closure = delegate 
    { 
     succeeded = outcome(); // if it worked, make as succeeded, else retry 
     Task.Delay(1000).ContinueWith(delegate 
     { 
      if (succeeded) 
       tcs.SetResult(succeeded); 
      else 
       closure(); 
     }, context); 
    }; 

    // start the task logic synchronously 
    // it could end synchronously too! (e.g, if we used 'Task.Delay(0)') 
    closure(); 

    return tcs.Task; 
} 

// start both tasks and handle the completion of each asynchronously 
private void StartWaitForItToWork() 
{ 
    WaitForItToWorkAsync().ContinueWith((t) => 
    { 
     MessageBox.Show("WaitForItToWorkAsync complete: " + t.Result.ToString()); 
    }, TaskScheduler.FromCurrentSynchronizationContext()); 

    WaitForItToWorkAsyncTap().ContinueWith((t) => 
    { 
     MessageBox.Show("WaitForItToWorkAsyncTap complete: " + t.Result.ToString()); 
    }, TaskScheduler.FromCurrentSynchronizationContext()); 
} 

// await for each tasks (StartWaitForItToWorkAsync itself is async) 
private async Task StartWaitForItToWorkAsync() 
{ 
    bool result = await WaitForItToWorkAsync(); 
    MessageBox.Show("WaitForItToWorkAsync complete: " + result.ToString()); 

    result = await WaitForItToWorkAsyncTap(); 
    MessageBox.Show("WaitForItToWorkAsyncTap complete: " + result.ToString()); 
} 

parçacığı üzerinde birkaç kelime. Burada açıkça oluşturulmuş ek bir konu yok.Dahili olarak, Task.Delay() uygulama havuz iş parçacığı kullanabilir (Timer Queues kullandıklarından şüpheleniyorum), ancak bu özel örnekte (bir WinForms uygulaması), await sonrasında devam aynı UI iş parçacığında gerçekleşir. Diğer uygulama ortamlarında (örneğin bir konsol uygulaması), farklı bir iş parçacığı üzerinde devam edebilir. Stephen Cleary tarafından IMO, this article, async/await iş parçacığı kavramlarını anlamak için okunmalıdır. Görev asenkron ise

+0

Bunu sadece this.WaitForItToWork() işlevini kullanarak söyler miyim? - async kitaplığı benim için iş parçacığıyla ilgilenir mi? – Chris

+1

Bunu, 'this.WaitForItToWork() '' ı beklemek gibi çağırırsınız ve bu çağrıyı desteklemek için tüm çağrı zincirleri yeniden düzenlenmelidir ... Daha fazla bilgi içermek için cevabımı ayrıntılı olarak ele alacağım. – Noseratio

+0

@Chris: "bekle" anahtar sözcüğünü kullanmayı unutmamalısınız. Genel kural: Her zaman "bekliyor", "async" işleviyle birleştirilmelidir. Yani, beklemek gibi bir şey yapmalısınız WaitForItToWork(); –

0

sizinle deneyebilirsiniz:

async Task WaitForItToWork() 
    { 
     await Task.Run(() => 
     { 
      bool succeeded = false; 
      while (!succeeded) 
      { 
       // do work 
       succeeded = outcome; // if it worked, make as succeeded, else retry 
       System.Threading.Thread.Sleep(1000); // arbitrary sleep 
      } 
     }); 
    } 

http://msdn.microsoft.com/en-us/library/hh195051.aspx bakınız. Gerçekten WaitItForWork yöntemi gerekmez

+1

Bu, cevabımla kaçınmak istediğim şey. Belki de kendimi bir şeyleri özlüyorum:] – Noseratio

+0

@Noseratio Async yöntemi, bir bekletme vurulduğunda hemen geri dönecektir. C# derleyicisi sizin için tüm iş parçacığı senkronizasyonunu yapar. http://msdn.microsoft.com/en-us/library/vstudio/hh156528.aspx Önemli olarak; "_An, ifade, yürütmekte olduğu iş parçacığı engellemez. Bunun yerine, derleyici beklenen görevde bir devamsızlık olarak async yönteminin geri kalanını imzalamak neden olur." – Gusdor

+0

Yanlış konuştu. Chillax. Gerçekleştikten sonra bile, netliğin düzeltilmesi veya eklenmesi konusunda hiçbir zararı yoktur. Diğer okuyucuların kafa karışıklığını önlemek için düzenledim. Seni aptallaştırmak için düzenlemedim. Bu DRY ilkesini ihlal ederdi./sickburn – Gusdor

0

, sadece veritabanı başlatma görev için bekliyor:

async Task Run() 
{ 
    await InitializeDatabase(); 
    // Do what you need after database is initialized 
} 

async Task InitializeDatabase() 
{ 
    // Perform database initialization here 
} 

Eğer bir Task içine veritabanı başlatma kaydırmak gerekir sonra WaitForItToWork çağrısı kod birden fazla parça varsa ve örneğin, tüm işçiler bunu bekliyor:

readonly Task _initializeDatabaseTask = InitializeDatabase(); 

async Task Worker1() 
{ 
    await _initializeDatabaseTask; 
    // Do what you need after database is initialized 
} 

async Task Worker2() 
{ 
    await _initializeDatabaseTask; 
    // Do what you need after database is initialized 
} 

static async Task InitializeDatabase() 
{ 
    // Initialize your database here 
} 
0

bir başka çözüm sağlamak

public static void WaitForCondition(Func<bool> predict) 
    { 
     Task.Delay(TimeSpan.FromMilliseconds(1000)).ContinueWith(_ => 
     { 
      var result = predict(); 
      // the condition result is false, and we need to wait again. 
      if (result == false) 
      { 
       WaitForCondition(predict); 
      } 
     }); 
    } 
İlgili konular