线程环境中的Redis不一致

时间:2016-09-23 09:48:11

标签: c# multithreading azure redis

在线程环境中运行时,我遇到了一些redis问题。

我有一个名为AwaitableParallelForeachWorker的类,我可以在其中为有效负载中的每个项目运行特定的函数。 (我知道它并不漂亮,但它能完成这项工作)

 public class AwaitableParallelForeachWorker : IAwaitableParallelForeachWorker
 {
    private readonly object _lockObject = new object();
    private int _tasksCompleted;

    public async Task Run<T>(Func<T, Task> action, IEnumerable<T> payload)
    {
        var list = payload.ToList();
        var tasks = list.Select(x => new Task(async () =>
        {
            await action(x);
            TaskDone();
        }));
        Parallel.ForEach(tasks, task => task.Start());
        while (_tasksCompleted < list.Count)
        {
            await Task.Delay(10);
        }
    }

    public void TaskDone()
    {
        lock (_lockObject)
        {
            _tasksCompleted++;
        }
    }
}

这里是redis缓存代码:

 public class NewsappRedisCache : INewsappRedisCache
 {
    private static readonly string ConnectionString = ConfigurationManager.AppSettings["RedisCache"];

    private static readonly Lazy<ConnectionMultiplexer> LazyConnection =
        new Lazy<ConnectionMultiplexer>(() => ConnectionMultiplexer.Connect(ConnectionString));

    private static IDatabase MimerArticleDatabase => Connection.GetDatabase(2);

    public async Task<MimerArticle> GetMimerArticleAsync(Guid id)
    {
        var redisValue = await MimerArticleDatabase.StringGetAsync($"{nameof(MimerArticle)}-{id}");
        if (!redisValue.HasValue) return null;
        var mimerArticle = JsonConvert.DeserializeObject<MimerArticle>(redisValue.ToString());
        return mimerArticle;
    } 

我写过这个简单的测试,使用AwaitableParallelForeachWorker调用我的redis缓存1000次

  public class Test
  {
    private NewsappRedisCache _redisCache;

    [Fact]
    public async void TestRedis()
    {
        var guids = new List<Guid>();

        for (var i = 0; i < 1000; i++)
        {
            guids.Add(Guid.NewGuid());
        }

        _redisCache = new NewsappRedisCache();
        await new AwaitableParallelForeachWorker().Run(CallRedis, guids);
    }

    private async Task CallRedis(Guid id)
    {
        await _redisCache.GetMimerArticleAsync(id);
    }
}

现在变得奇怪了。有时1000秒到达redis缓存会在瞬间执行。我已经通过检查azure门户网站验证了实际上达到了缓存。但有时每次获得大约需要1秒钟。

我不知道为什么。我曾尝试更改AwaitableParallelForeachWorker的功能,但它仍然不一致。 如果我运行每个get in a normal foreach它执行得很好,但没有AwaitableParallelForeachWorker实际工作时那么快。

所以我觉得它与线程/任务有关。

任何可以提供帮助的人?

1 个答案:

答案 0 :(得分:1)

我找不到确切的原因,但我也不能在评论中说明这一点,所以这里有一些事情需要考虑:

  • 测试方法不应该是async Task,而不是async void吗?一些测试框架处理这个可以说是不正确的用法,但也许你的用法没有。如果框架无法知道方法何时完成,那么它会认为一切都是在第一次完成时完成的。

  • 您的Run()方法可以大大简化为:

    public Task Run<T>(Func<T, Task> action, IEnumerable<T> payload)
    {
        return Task.WhenAll(payload.Select(action));
    }
    

    并摆脱课堂上的其他人。

  • 您的CallRedis()方法不需要状态机开销;代替:

    private Task CallRedis(Guid id)
    {
        return _redisCache.GetMimerArticleAsync(id);
    }
    
  • 虽然我在为你节省行数:

    var guids = Enumerable.Range(0, 1000).Select(Guid.NewGuid()).ToList();
    // :)