The underlying database connections that the Entity Framework are using are not thread-safe。你将需要为您要执行的另一个线程上的每个操作创建一个新的上下文。
您对如何并行化操作的担忧是有效的;许多情况下打开和关闭都很昂贵。
相反,您可能想要颠倒您对并行代码的思考。看起来你正在循环多个项目,然后为每个项目串行调用存储过程。
如果可以的话,创建一个新的Task<TResult>
(或Task
,如果你不需要的结果)每个程序然后在该Task<TResult>
,通过所有的项目的开单情况下,循环,然后执行存储过程。这样,您只有许多上下文等于您并行运行的存储过程的数量。
让我们假设你有两个存储过程,DoSomething1
和DoSomething2
,两者取一类,MyItem
实例的MyDbContext
。
实现上述看起来是这样的:
// 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.
如果无法并行执行存储过程(每一个依赖于一定的秩序中运行),那么你仍然可以并行化您的操作,它只是更复杂一点。
你会看到你的项目上creating custom partitions(使用Partitioner
class上的静态Create
method)。这将给你的手段获得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服务器将无助于性能... – rene
SQL没有强调所有,这就是为什么我想使用并行。它肯定会跑得更快。 –