3

数百万行のデータに対して C# 計算を 1 回実行し、結果を別のテーブルに保存する必要があります。私はここ数年、C# でスレッド化を行っていません。.NET v4.5 と EF v5 を使用しています。

元のコードは次のようなものです。

public static void Main()
{
    Stopwatch sw = new Stopwatch();
    sw.Start();
    Entities db = new Entities();
    DoCalc(db.Clients.ToList());
    sw.Stop();
    Console.WriteLine(sw.Elapsed);
}

private static void DoCalc(List<Client> clients)
{
Entities db = new Entities();    
    foreach(var c in clients)
    {
       var transactions = db.GetTransactions(c);
       var result = calulate(transactions); //the actual calc
       db.Results.Add(result);
       db.SaveChanges();
    }    
}

マルチスレッドでの私の試みは次のとおりです。

private static int numberOfThreads = 15;

public static void Main()
{
    Stopwatch sw = new Stopwatch();
    sw.Start();
    Entities db = new Entities();

    var splitUpClients = SplitUpClients(db.Clients());

    Task[] allTasks = new Task[numberOfThreads];

    for (int i = 0; i < numberOfThreads; i++)
    {               
        Task task = Task.Factory.StartNew(() => DoCalc(splitupClients[i]));
        allTasks[i] = task;             
     }  

    Task.WaitAll(allTasks);             
    sw.Stop();
    Console.WriteLine(sw.Elapsed);
}

private static void DoCalc(List<Client> clients)
{
Entities db = new Entities();    
    foreach(var c in clients)
    {
       var transactions = db.GetTransactions(c);
       var result = calulate(transactions);
       db.Results.Add(result);
       db.SaveChanges();
    }    
}

//splits the list of clients into n subgroups
private static List<List<Client>> SplitUpClients(List<Client> clients)
{
    int maxPerGroup = (int)Math.Ceiling((double)clients.Count() / numberOfThreads);

    return ts.Select((s, i) => new { Str = s, Index = i }).
                        GroupBy(o => o.Index / maxPerGroup, o => o.Str).
                        Select(coll => coll.ToList()).
                        ToList();           
}

私の質問は:

これは安全で正しい方法ですか?(特に EF に関して)明らかな欠点はありますか?

また、最適なスレッド数を見つけるにはどうすればよいですか? 多ければ多いほど楽しいですか?

4

2 に答える 2

7

エンティティ フレームワークのDbContextクラスとObjectContextクラスは、スレッド セーフではありません。したがって、複数のスレッドで使用しないでください。

エンティティを他のスレッドに渡すだけのように見えますが、遅延読み込みが関係している場合、これを間違えるのは簡単です。これは、エンティティが内部でコンテキストにコールバックして、さらにデータを取得することを意味します。

代わりに、エンティティのリストを、計算に必要なデータのみを必要とする特別な不変データ構造のリストに変換することをお勧めします。これらの不変の構造は、コンテキストにコールバックする必要はなく、変更できないようにする必要があります。これを行うと、計算を行うためにそれらを他のスレッドに渡しても安全です。

于 2013-08-28T10:20:14.407 に答える