c# - 从 Redis 检索多个键时死锁

标签 c# async-await task-parallel-library stackexchange.redis

我正在尝试将此查询的结果并行获取到 redis(使用 stackexchange C# 客户端),但不知何故我在死锁中运行并且不确定在哪里

获取数据的方法:

public LiveData Get(string sessionId)
{
    return GetAsync(sessionId).Result;
}

private async Task<LiveData> GetAsync(string sessionId)
{
    var basketTask = GetBasketAsync(sessionId);
    var historyTask = GetHistoryAsync(sessionId);
    var capturedDataTask = GetCapturedDataAsync(sessionId);

    var basket = await basketTask;
    var history = await historyTask;
    var capturedData = await capturedDataTask;

    return new LiveData
    {
        Basket = basket.IsNullOrEmpty
            ? new List<Product>()
            : JsonConvert.DeserializeObject<List<Product>>(basket),
        History = history.Select(cachedProduct
            => JsonConvert.DeserializeObject<Product>(cachedProduct.Value.ToString())).ToList(),
        CapturedData = capturedData.ToDictionary<HashEntry, string, object>(
            hash => hash.Name, hash => JsonConvert.DeserializeObject(hash.Value))
    };
}

而从redis中获取缓存数据的方法是:

private async Task<RedisValue> GetBasketAsync(string key)
{
    key = $"{key}{BasketSuffix}";
    var redisDb = RedisConnection.Connection.GetDatabase();
    redisDb.KeyExpireAsync(key, _expire);

    return await redisDb.StringGetAsync(key);
}

private async Task<HashEntry[]> GetHistoryAsync(string key)
{
    key = $"{key}{HistorySuffix}";
    var redisDb = RedisConnection.Connection.GetDatabase();
    redisDb.KeyExpireAsync(key, _expire);

    return await redisDb.HashGetAllAsync(key);
}

private async Task<HashEntry[]> GetCapturedDataAsync(string key)
{
    key = $"{key}{CapturedDataSuffix}";
    var redisDb = RedisConnection.Connection.GetDatabase();
    redisDb.KeyExpireAsync(key, _expire);

    return await redisDb.HashGetAllAsync(key);
}

我认为这样调用 KeyExpireAsync 很好,只是因为它可以触发并忘记但不确定这是否相关(我什至尝试删除它但它仍然被阻止)

最佳答案

死锁的根源是这个片段:

public LiveData Get(string sessionId)
{
    return GetAsync(sessionId).Result;
}

相反,以“一直异步”的正确方式调用它:

public async Task<LiveData> Get(string sessionId)
{
    return await GetAsync(sessionId);
}

调用 .Result 会导致死锁,使用 .Wait() API 也会导致死锁。此外,从外观上看 -- .KeyExpireAsync 需要等待。

async Task<RedisValue> GetBasketAsync(string key)
{
    key = $"{key}{BasketSuffix}";
    var redisDb = RedisConnection.Connection.GetDatabase();
    await redisDb.KeyExpireAsync(key, _expire);

    return await redisDb.StringGetAsync(key);
}

我理解您在 .KeyExpireAsync 调用中不使用 await 关键字的想法,但如果我正在编写这段代码,我肯定会希望 await 就像我演示的那样。即发即弃是一种代码味道,可以轻松避免。

关于c# - 从 Redis 检索多个键时死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38305645/

相关文章:

c# - 如何获取 ServiceBusClient 的连接字符串?

javascript - 仅在 Javascript forEach/map 之后执行代码

javascript - 在新的 Promise() 构造函数中使用 async/await 是一种反模式吗?

c# - 在调用之间暂停的多个异步调用

c# - 在匿名方法中执行任务时捕获抛出的异常

.net - 使用 TPL 时如何管理线程本地存储 (TLS)?

c# - 适当使用 BlockingCollection

c# - Sprache:语法中的左递归

c# - 初始化填充有相同元素的数组

c# - 阻止 Visual Studio 2008 重写内联代码?