2012-11-23 19 views
9

Bir Windows 8 projesi için bir async hizmeti ile etrafta dolaşıyorum ve bu hizmetin bazı async çağrıları vardır, bunlar sadece bir kerede bir kez çağrılmalıdır. Bir Görevde birden fazla bekçisi olabilir mi?

public async Task CallThisOnlyOnce() 
{ 
     PropagateSomeEvents(); 

     await SomeOtherMethod(); 

     PropagateDifferentEvents(); 
} 

Bir kilit açıklamada bir zaman uyumsuz çağrı encapsulate olamayacağına göre

i AsyncLock kalıbı kullanarak düşündüm ama düşündüğümden daha ben de böyle bir şey deneyebilirsiniz: Dolayısıyla

private Task _callThisOnlyOnce; 
public Task CallThisOnlyOnce() 
{ 
     if(_callThisOnlyOnce != null && _callThisOnlyOnce.IsCompleted) 
     _callThisOnlyOnce = null; 

     if(_callThisOnlyOnce == null) 
     _callThisOnlyOnce = CallThisOnlyOnceAsync(); 

     return _callThisOnlyOnce; 
} 

private async Task CallThisOnlyOnceAsync() 
{ 
     PropagateSomeEvents(); 

     await SomeOtherMethod(); 

     PropagateDifferentEvents(); 
} 

sen olur Yalnızca bir kez simultane bir şekilde yürütülen CallThisOnlyOnceAsync numaralı çağrıyla sona erdirilir ve aynı görevde birden fazla bekletici de bağlanır.

Bu "geçerli" bir yöntem midir, yoksa bu yaklaşımın bazı dezavantajları var mı?

cevap

6

Bir görevin birden çok bekçisi olabilir. Ancak, Damien'ın işaret ettiği gibi, önerilen kodunuzla ciddi yarış koşulları var.

Yönteminizin her çağrıldığında (ancak aynı anda değil) kodun yürütülmesini istiyorsanız, AsyncLock kullanın. Kodun yalnızca bir kez yürütmesini istiyorsanız, AsyncLazy'u kullanın.

Önerilen çözümünüz, birden fazla çağrıyı birleştirmeyi deniyor ve zaten çalışmıyorsa kodu yeniden çalıştırmayı deniyor. Bu daha zordur ve çözüm, ihtiyaç duyduğunuz kesin anlamlara bağlıdır. İşte bir seçenek:

private AsyncLock mutex = new AsyncLock(); 
private Task executing; 

public async Task CallThisOnlyOnceAsync() 
{ 
    Task action = null; 
    using (await mutex.LockAsync()) 
    { 
    if (executing == null) 
     executing = DoCallThisOnlyOnceAsync(); 
    action = executing; 
    } 

    await action; 
} 

private async Task DoCallThisOnlyOnceAsync() 
{ 
    PropagateSomeEvents(); 

    await SomeOtherMethod(); 

    PropagateDifferentEvents(); 

    using (await mutex.LockAsync()) 
    { 
    executing = null; 
    } 
} 

Ayrıca Interlocked ile bunu yapmak mümkün, ama bu kod çirkin olur.

P.S. Benim AsyncEx library benim AsyncLock, AsyncLazy ve diğer async Zaten ilkel var.

+0

Her iki cevabı da beğendim, ancak bir uygulama önerisi eklediğiniz için sizinkini seçtim. Ayrıca, kitaplığınızı Nuget kullanarak yüklemeyi denedim, ancak Windows Mağazası projemde başarısız oldu (Microsoft.Bcl.Async dosyasını çözemedi) – UrbanEsc

+1

"Prerelease dahil" onay kutusunu işaretlemeyi deneyin. Paketim yayın öncesi (ancak NuGet bunu doğru olarak algılamıyor) ve 'Microsoft.Bcl.Async' de (NuGet'in doğru şekilde algıladığı) yayın öncesidir. –

4

Birden fazla iş parçacığı söz konusu olduğunda bu kod çok "kulağa" benziyor.

Bir örnek (Eminim daha fazlası vardır). _callThisOnlyOnce anda null olduğunu varsayın:

Thread 1               Thread 2 

public Task CallThisOnlyOnce() 
{ 
    if(_callThisOnlyOnce != null && _callThisOnlyOnce.IsCompleted) 
    _callThisOnlyOnce = null; 

    if(_callThisOnlyOnce == null) 
                    public Task CallThisOnlyOnce() 
                    { 
                    if(_callThisOnlyOnce != null && _callThisOnlyOnce.IsCompleted) 
                     _callThisOnlyOnce = null; 

                    if(_callThisOnlyOnce == null) 
                     _callThisOnlyOnce = CallThisOnlyOnceAsync(); 

                    return _callThisOnlyOnce; 
                    } 
    _callThisOnlyOnce = CallThisOnlyOnceAsync(); 

    return _callThisOnlyOnce; 
} 

Artık aynı anda çalışan 2 aramaları var.

Birden çok bekleyiş konusunda, bunu yapabilirsiniz. MS'den bir örnek kodun, örn. Task.FromResult(0) sonucu statik bir üyede saklanır ve işlev sıfırlamak istediği zaman döndürülür.

Ancak bu kod örneğini bulmakta başarısız oldum.

İlgili konular