The underlying database connections that the Entity Framework are using are not thread-safe. È necessario creare un nuovo contesto per ogni operazione su un altro thread che si sta per eseguire.
La tua preoccupazione su come parallelizzare l'operazione è valida; che molti contesti saranno costosi da aprire e chiudere.
Invece, potresti voler invertire la tua opinione sulla parallelizzazione del codice. Sembra che tu stia eseguendo il loop su un numero di elementi e poi chiamando le stored procedure in serie per ciascun elemento.
Se è possibile, creare un nuovo Task<TResult>
(o Task
, se non hai bisogno di un risultato) per ogni procedura di e poi in quella Task<TResult>
, aprire un unico contesto, il ciclo di tutti gli elementi, e quindi eseguire la stored procedure. In questo modo, hai solo un numero di contesti uguale al numero di stored procedure che stai eseguendo in parallelo.
Supponiamo di avere un MyDbContext
con due stored procedure, DoSomething1
e DoSomething2
, entrambi i quali assumono un'istanza di una classe, MyItem
.
Attuare quanto sopra sarebbe simile:
// 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.
Se non si può eseguire le stored procedure in parallelo (ognuno è dipendente da essere investito in un certo ordine), allora si può ancora parallelizzare le operazioni, è solo un po 'più complesso.
Si guarderebbe creating custom partitions attraverso i propri articoli (utilizzando lo statico Create
method su Partitioner
class). Questo ti darà i mezzi per ottenere le implementazioni IEnumerator<T>
(nota, questo è nonIEnumerable<T>
quindi non puoi farlo su foreach
).
Per ogni IEnumerator<T>
esempio torni, devi creare un nuovo Task<TResult>
(se avete bisogno di un risultato), e nel corpo Task<TResult>
, si potrebbe creare il contesto e poi scorrere le voci restituite dalla IEnumerator<T>
, chiamando le stored procedure in ordine.
che sarebbe simile a questa:
// 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();
Io non sono un guru multithreading, ma se si utilizzano le transazioni o la lettura/scrittura viene bloccata, è possibile che non si ottengano prestazioni migliori rispetto alla semplice esecuzione seriale. – Matthew
Dubito che martellare il server SQL stressato con più thread non aiuti le prestazioni ... – rene
Lo sql non è affatto stressato, ecco perché voglio usare un parallelo. e funzionerà sicuramente più velocemente. –