The underlying database connections that the Entity Framework are using are not thread-safe。 は、実行する別のスレッドで操作ごとに新しいコンテキストを作成する必要があります。
操作を並列化する方法についてのあなたの懸念は有効です。多くの文脈が開いたり閉じたりするのに高価になるだろう。
代わりに、コードを並列化する方法を逆にしたい場合があります。それはあなたがいくつかの項目をループしていると、各項目のためにシリアルでストアドプロシージャを呼び出しているようです。その後、
(あなたが結果を必要としない場合、またはTask
)あなたは、各手順のための新しいTask<TResult>
を作成できるかどうかと、そのTask<TResult>
に、単一のコンテキストを開いて、すべてのアイテムをループし、ストアドプロシージャを実行します。このようにして、実行中のストアドプロシージャの数と同じ数のコンテキストのみが並行して実行されます。
は、あなたがクラスのインスタンスを取り、どちらも2羽のストアドプロシージャ、DoSomething1
とDoSomething2
、とMyDbContext
、MyItem
を持っていると仮定しましょう。あなたはが並行してストアドプロシージャを(それぞれが特定の順序で実行されているに依存します)を実行できない場合
// You'd probably want to materialize this into an IList<T> to avoid
// warnings about multiple iterations of an IEnumerable<T>.
// You definitely *don't* want this to be an IQueryable<T>
// returned from a context.
IEnumerable<MyItem> items = ...;
// The first stored procedure is called here.
Task t1 = Task.Run(() => {
// Create the context.
using (var ctx = new MyDbContext())
// Cycle through each item.
foreach (MyItem item in items)
{
// Call the first stored procedure.
// You'd of course, have to do something with item here.
ctx.DoSomething1(item);
}
});
// The second stored procedure is called here.
Task t2 = Task.Run(() => {
// Create the context.
using (var ctx = new MyDbContext())
// Cycle through each item.
foreach (MyItem item in items)
{
// Call the first stored procedure.
// You'd of course, have to do something with item here.
ctx.DoSomething2(item);
}
});
// Do something when both of the tasks are done.
、その後、あなたはまだすることができ:
のような上記になります何かを実装しますあなたの操作を並列化してください。ちょっと複雑です。
Partitioner
classの静的Create
methodを使用すると、アイテム全体でcreating custom partitionsが表示されます。これにより、IEnumerator<T>
実装を取得する手段が提供されます(これはではありません。IEnumerable<T>
なのでforeach
にはできません)。
あなたが戻って取得各IEnumerator<T>
たとえば、あなたが作成したい新しいTask<TResult>
(あなたは結果が必要な場合)、およびTask<TResult>
ボディに、あなたが呼び出し、IEnumerator<T>
によって返された項目によるコンテキストと、その後のサイクルを作成しますストアドプロシージャを順番に実行します。
// Get the partitioner.
OrdinalPartitioner<MyItem> partitioner = Partitioner.Create(items);
// Get the partitions.
// You'll have to set the parameter for the number of partitions here.
// See the link for creating custom partitions for more
// creation strategies.
IList<IEnumerator<MyItem>> paritions = partitioner.GetPartitions(
Environment.ProcessorCount);
// Create a task for each partition.
Task[] tasks = partitions.Select(p => Task.Run(() => {
// Create the context.
using (var ctx = new MyDbContext())
// Remember, the IEnumerator<T> implementation
// might implement IDisposable.
using (p)
// While there are items in p.
while (p.MoveNext())
{
// Get the current item.
MyItem current = p.Current;
// Call the stored procedures. Process the item
ctx.DoSomething1(current);
ctx.DoSomething2(current);
}
})).
// ToArray is needed (or something to materialize the list) to
// avoid deferred execution.
ToArray();
私はマルチスレッドの専門家ではありませんが、トランザクションを使用している場合や読み書きがロックされている場合は、直列的に行うよりもパフォーマンスが向上しない場合があります。 – Matthew
私は、より多くのスレッドで強調されたSQL Serverを打つことはパフォーマンスに役立つことはないとは思わない... – rene
SQLは全くストレスがかからず、なぜ並列を使用したいのか?それは確かに速く実行されます。 –