我可以使用Parallel.For和sql命令吗?

我有一类范围

public class avl_range { public long start { get; set; } public long end { get; set; } } 

如果我使用正常的FOR工作完美,但必须等待每个命令完成,每个查询需要8秒,所以10个查询需要80秒。

在Parallel版本中如果我只打印范围工作完美,但如果尝试执行命令说已经在进行中。

{“一项操作已在进行中。”}

我怎么解决这个问题?

 var numbers = new List(); using (var conn = new NpgsqlConnection(strConnection)) { conn.Open(); Action forEachLoop = number => //Begin definition of forLoop { // only the console write line works ok Console.WriteLine(number.start + " - " + number.end); using (var cmd = new NpgsqlCommand()) { cmd.Connection = conn; cmd.CommandText = String.Format( "SELECT * FROM avl_db.process_near_link({0}, {1});" , number.start , number.end); // here cause the error. using (var reader = cmd.ExecuteReader()) { while (reader.Read()) { Console.WriteLine(reader.GetString(0)); } } } }; Parallel.ForEach(numbers, forEachLoop); } ); 

仅供参考:我试图解决这个问题,我之前发布过

不能同时使用Npgsql连接 – 在任何给定的时间点只能运行一个命令(换句话说,没有MARS支持)。

打开多个连接以并行执行查询肯定是有意义的。 尽管建立新的物理连接非常昂贵,但连接池非常轻量级,因此重用物理连接的开销非常小。 不这样做的主要原因是,如果您需要多个操作在同一个事务中。

即使您可以使用MARS,连接对象几乎从不是线程安全的,您需要为每个线程建立连接。 Parallel.ForEach有重载使得这很容易 ,它具有在线程开始和结束时运行的函数。

 var numbers = new List(); Func localInit => () => { var conn = new NpgsqlConnection(strConnection); conn.Open(); }; Action localFinally = (conn) => conn.Dispose(); Func forEachLoop = (number, loopState, conn) => //Begin definition of forLoop { // only the console write line works ok Console.WriteLine(number.start + " - " + number.end); using (var cmd = new NpgsqlCommand()) { cmd.Connection = conn; cmd.CommandText = String.Format( "SELECT * FROM avl_db.process_near_link({0}, {1});" , number.start , number.end); // here cause the error. using (var reader = cmd.ExecuteReader()) { while (reader.Read()) { Console.WriteLine(reader.GetString(0)); } } } return conn; }; Parallel.ForEach(numbers, localInit, forEachLoop, localFinally); 

话虽这么说,大部分时间与数据库并发连接都不是正确的想法,瓶颈可能在其他地方,你应该使用分析器来查看什么真正减慢你的程序,并集中你的努力。


评论示例代码:

 var numbers = GetDataForNumbers(); List results = new List(); Func> localInit => () => new List(); Func, List> forEachLoop = (number, loopState, localList) => //Begin definition of forLoop { using (var conn = new NpgsqlConnection(strConnection)) { conn.Open(); //This line is going to slow your program down a lot, so i commented it out. //Console.WriteLine(number.start + " - " + number.end); using (var cmd = new NpgsqlCommand()) { cmd.Connection = conn; cmd.CommandText = String.Format( "SELECT * FROM avl_db.process_near_link({0}, {1});" , number.start , number.end); using (var reader = cmd.ExecuteReader()) { while (reader.Read()) { //Add a object to the thread local list, we don't need to lock here because we are the only thread with access to it. localList.Add(reader.GetString(0)); } } } } return localList; }; Action> localFinally = localList => { //Combine the local list to the main results, we need to lock here as more than one thread could be merging at once. lock(results) { results.AddRange(localList); } }; Parallel.ForEach(numbers, localInit, forEachLoop, localFinally); //results now contains strings from all the threads here.