18

我有这段代码可以在 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();
4

3 回答 3

24

在事务中执行的命令在执行事务之前不会返回结果。这只是 Redis 中事务如何工作的一个特性。目前,您正在等待尚未发送的内容(事务在本地缓冲直到执行) - 但即使它已发送:在事务完成之前结果根本不可用。

如果你想要结果,你应该存储(而不是等待)任务,并在执行等待它:

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

请注意,这比看起来要便宜:当事务执行时,嵌套任务会同时获得它们的结果 - 并await有效地处理该场景。

于 2014-09-22T15:39:11.063 回答
7

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)没关系 - 我总是使用事务进行一系列写入。我发现这确实有助于精简我的代码,并且只暴露tran内部Enqueue(这需要你返回一个任务),我不太可能“忘记”await在我调用这些命令时我不应该使用这些命令。

于 2018-02-15T18:47:54.653 回答
4

我和我们的团队多次被这个问题困扰,所以我创建了一个简单的 Roslyn 分析器来发现这些问题。

https://github.com/olsh/stack-exchange-redis-analyzer

于 2018-12-06T22:54:11.380 回答