2014-09-22 24 views
5

Stackexchange.Redis'te nesne ve dizin alanı eklemek için bu kodu kullanıyorum. Tüm işlemler işlem dondurma ipinde. Neden? bir hareket içinde yürütülenStackExchange.Redis işlem yöntemleri donuyor

var transaction = Database.CreateTransaction(); 

    //this line freeze thread. WHY ? 
    await transaction.StringSetAsync(KeyProvider.GetForID(obj.ID), PreSaveObject(obj)); 
    await transaction.HashSetAsync(emailKey, new[] { new HashEntry(obj.Email, Convert.ToString(obj.ID)) }); 

    return await transaction.ExecuteAsync(); 
+0

Henüz hazır bulunamadığım için: burada "sıraya alındı" sayfasına bakın: http://redis.io/topics/transactions –

cevap

11

Komutları sonra işlem yürütmek kadar sonuç döndürmez. Bu, işlemlerin Redis'te nasıl işlediğinin bir özelliğidir. Şu anda henüz gönderilmemiş bir şeyi bekliyorsunuz (işlemler icra edilinceye kadar lokal olarak işlenir) - ancak gönderilse bile: sonucu işlem tamamlanana kadar kullanılabilir değildir. Eğer sonuç istiyorsanız

, siz (bekliyor değil) görevi saklayabilir ve o sonra bekliyor gerektiğini yürütün:

var fooTask = tran.SomeCommandAsync(...); 
if(await tran.ExecuteAsync()) { 
    var foo = await fooTask; 
} 

bu göründüğünden daha ucuzdur Not olun: işlem yürütüldüğünde, İç içe geçmiş görevler sonuçları aynı anda alır ve await bu senaryoyu verimli bir şekilde ele alır.

+2

Garip mantık, ama işe yarıyor! Teşekkür ederim! – boostivan

+0

@boostivan alışmak için biraz düşünmek; Not - Bunu yapmanın başka bir yolu, "Script *" işlevini kullanmak ve sunucuda tüm işlemleri yapan bir Lua komut dosyasını göndermek. Çok daha basit. –

+0

@MarcGravell Komut (lar) ın sonucuna/bileşenlerine ihtiyacım varsa, en iyi uygulama hangisidir? Görevleri yakalayın ve işlemden sonra onları bekliyor ya da ateş ve unuyor musunuz? (Eskiyi tahmin et ama emin olmak istedim.) –

0

Marc'ın cevabı işe yarıyor, ancak benim durumumda, iyi bir kod bloğu miktarına neden oldu (ve bu şekilde yapmayı unutmak kolay), bu yüzden, deseni zorlayan bir soyutlama ile geldim.

public static class RedisExtensions 
{ 
    public static async Task TransactAsync(this IDatabase db, Action<RedisCommandQueue> addCommands) 
    { 
     var tran = db.CreateTransaction(); 
     var q = new RedisCommandQueue(tran); 

     addCommands(q); 

     if (await tran.ExecuteAsync()) 
      await q.CompleteAsync(); 
    } 
} 

public class RedisCommandQueue 
{ 
    private readonly ITransaction _tran; 
    private readonly IList<Task> _tasks = new List<Task>(); 

    public RedisCommandQueue Enqueue(Func<ITransaction, Task> cmd) 
    { 
     _tasks.Add(cmd(_tran)); 
     return this; 
    } 

    internal RedisCommandQueue(ITransaction tran) => _tran = tran; 
    internal Task CompleteAsync() => Task.WhenAll(_tasks); 
} 

Bir ihtar: Bu herhangi birinin sonucu almak için kolay bir yol sağlamaz İşte

await db.TransactAsync(commands => commands 
    .Enqueue(tran => tran.SomeCommandAsync(...)) 
    .Enqueue(tran => tran.SomeCommandAsync(...)) 
    .Enqueue(tran => tran.SomeCommandAsync(...))); 

uygulama görebilirsiniz: İşte

Kullanmaya nasıl emreder. Benim durumumda (ve OP'ler) o tamam - Ben her zaman bir dizi yazım için işlemleri kullanıyorum. Bu gerçekten benim kod aşağı kesme kesilmiş ve Enqueue (bir Görev döndürmek için) gerektiren tran açığa, bu anda o komutları await olmamalıdır "unutma" daha az olası olduğunu buldum Ben ararım onları.

İlgili konular