2014-09-22 96 views
5

我有這段代碼在Stackexchange.Redis中添加對象和索引字段。 事務凍結線程中的所有方法。 爲什麼?在事務內部執行StackExchange.Redis事務方法凍結

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

我的意思是暫時還沒有提供:請參閱這裏的「排隊」:http://redis.io/topics/transactions – 2014-09-22 16:16:01

回答

11

命令不返回,直到後你執行的交易結果。這只是Redis中事務如何工作的一個特性。目前您正在等待尚未發送的內容(交易在本地緩存直至執行) - 但即使已發送:結果根本不可用,直到事務完成。

如果你想要的結果,你應該存儲(不等待)的任務,並等待它後的執行:

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

注意,這是價格比它看起來:交易執行時,嵌套任務可以同時獲得結果 - 並且await可以有效地處理這種情況。

+2

奇怪的邏輯,但它的工作原理!謝謝! – boostivan 2014-09-23 12:47:28

+0

@boostivan它需要一些思考才能習慣;注意 - 另一種方法是使用'Script *'併發送一個Lua腳本來完成操作。簡單得多。 – 2014-09-23 12:50:33

+0

@MarcGravell如果我不需要命令的結果,最好的做法是什麼?在交易完成之後捕獲任務並「等待」它們,或者開火併忘記? (猜測前者,但只是想確定。) – 2018-02-15 16:36:08

0

Marc的答案很有用,但在我的情況下,它導致了大量的代碼膨脹(並且很容易忘記這樣做),所以我想出了一種抽象來實現這種模式。

這裏是你如何使用它:

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

這裏的實現:

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); 
} 

警告:這並沒有提供一種簡單的方式來獲得在任何的結果命令。在我的情況下(和OP的)沒關係 - 我總是使用事務進行一系列寫操作。我發現這確實有助於削減我的代碼,並且只在Enqueue(它要求您返回任務)內暴露tran,我不太可能「忘記」我當時不應該使用這些命令我打電話給他們。