从Redis检索多个键时死锁

时间:2016-07-11 11:19:32

标签: 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很好,只是因为它可以触发并忘记,但不确定是否可以相关(我尝试删除它并且它仍然被阻止)

1 个答案:

答案 0 :(得分:3)

死锁的来源就是这个片段:

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

相反,以正确的方式调用它&#34;一直异步&#34;:

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

我理解您在await调用时未使用.KeyExpireAsync关键字的思考过程,但如果我编写此代码,我肯定希望await就像我演示的那样。这是一种代码气味,可以轻松避免。