【问题标题】:StackExchange.Redis transaction methods freezesStackExchange.Redis 事务方法冻结
【发布时间】:2014-11-16 13:06:26
【问题描述】:

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

【问题讨论】:

标签: redis stackexchange.redis


【解决方案1】:

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

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

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

请注意,这比看起来要便宜:当事务执行时,嵌套任务会同时获得它们的结果 - 而await 可以有效地处理这种情况。

【讨论】:

  • @boostivan 确实需要一些思考才能习惯;注意 - 另一种方法是使用Script* 并发送一个在服务器上执行所有操作的 Lua 脚本。简单得多。
  • @MarcGravell 如果我需要命令的结果,最佳做法是什么?无论如何,在事务之后捕获任务和await它们,还是解雇并忘记? (猜测前者,但只是想确定。)
  • @MarcGravell 您将如何在同一事务中的另一操作中使用一个操作的结果?你能回答这个question
【解决方案2】:

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(这需要你返回一个任务),我不太可能“忘记”我不应该是awaiting我调用它们时的那些命令。

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-10-19
      • 2021-05-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多