2016-09-23 10 views
0

スレッド環境で実行しているときにredisに問題があります。スレッド環境でのRedisが矛盾しています

私はAwaitableParallelForeachWorkerというクラスを持っています。このクラスでは、ペイロード内の各アイテムに対して特定の関数を実行できます。私は私のRedisのキャッシュAwaitableParallelForeachWorker

を使用して1000倍を呼び出して、この簡単なテストを書かれている

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

:ここ

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

今は変です。時には、redisキャッシュへの1000回の取得が分割されて実行されることもあります。私はazureポータルをチェックして実際にキャッシュにヒットしたことを確認しました。しかし、時にはそれぞれの取得に約1秒かかります。

私には理由が分かりません。私はAwaitableParallelForeachWorkerの機能を変更しようとしましたが、矛盾しています。 もし私がそれぞれ通常のforeachを実行すると、AwaitableParallelForeachWorkerが実際に動作したときと同じくらい速く実行されます。

私はそれがスレッド/タスクと関係があると思って立ち往生しています。

助けてもらえますか?

私は正確な理由を見つけることができませんが、私は考慮すべきいくつかのものですので、ここで、いずれかのコメントでこれをフィットすることはできません

答えて

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(); 
    // :) 
    
+0

Task.WhenAll(payload.Select(アクション))への変更は、トリックをしました! 私はこれまで、以下を使用していました:return Task.WhenAll(payload.Select(async x => await action(x)))、それは私が作った奇妙なRunメソッドと同じでした。だからこそ私はRunをそのように実装したのです...私は明らかに非同期ではなく、まだまだ待っています。 Anwyaysは本当にありがとう! – Boenne

関連する問題