boo*_*van 11 redis 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();
Run Code Online (Sandbox Code Playgroud)
Mar*_*ell 17
在执行事务之前,在事务内执行的命令不会返回结果.这只是Redis中交易如何工作的一个特征.此刻,您正在等待尚未发送的内容(事务在本地缓冲,直到执行) - 但即使已发送:结果在事务完成之前根本不可用.
如果你想要结果,你应该存储(而不是等待)任务,并在执行后等待它:
var fooTask = tran.SomeCommandAsync(...);
if(await tran.ExecuteAsync()) {
var foo = await fooTask;
}
Run Code Online (Sandbox Code Playgroud)
请注意,这比它看起来要便宜:当事务执行时,嵌套任务会同时获得结果 - 并await有效地处理该场景.
Marc 的回答有效,但在我的情况下,它导致了大量的代码膨胀(而且很容易忘记这样做),所以我想出了一个抽象来强制执行该模式。
以下是您如何使用它:
await db.TransactAsync(commands => commands
.Enqueue(tran => tran.SomeCommandAsync(...))
.Enqueue(tran => tran.SomeCommandAsync(...))
.Enqueue(tran => tran.SomeCommandAsync(...)));
Run Code Online (Sandbox Code Playgroud)
这是实现:
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);
}
Run Code Online (Sandbox Code Playgroud)
一个警告:这并没有提供一种简单的方法来获得任何命令的结果。在我的情况下(和 OP),没关系 - 我总是使用事务进行一系列写入。我发现这确实有助于精简我的代码,并且仅通过暴露tran内部Enqueue(这需要您返回任务),我不太可能“忘记”我不应该await在调用它们时执行这些命令。
我和我们的团队多次被这个问题困扰,因此我创建了一个简单的 Roslyn 分析器来发现此类问题。
https://github.com/olsh/stack-exchange-redis-analyzer
| 归档时间: |
|
| 查看次数: |
2638 次 |
| 最近记录: |