我有一类范围
public class avl_range
{
public long start { get; set; }
public long end { get; set; }
}
如果我使用正常FOR
作品完美,但必须等待每个命令完成,每个查询需要8秒,所以10个查询需要80秒。
在Parallel版本中如果我只打印范围工作完美,但如果尝试执行命令则说明已经在进行中。
{"某项操作已在进行中。"}
我该如何解决这个问题?
var numbers = new List<avl_range>();
using (var conn = new NpgsqlConnection(strConnection))
{
conn.Open();
Action<avl_range> 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);
}
);
仅供参考:我试图解决这个问题,我发布了before
答案 0 :(得分:4)
不能同时使用Npgsql连接 - 在任何给定的时间点只能运行一个命令(换句话说,没有MARS支持)。
打开多个连接以并行执行查询肯定是有意义的。尽管建立新的物理连接非常昂贵,但连接池非常轻量级,因此重用物理连接的开销非常小。不这样做的主要原因是,如果您需要多个操作在同一个事务中。
答案 1 :(得分:3)
即使您可以使用MARS,连接对象也几乎从不是线程安全的,您需要为每个线程建立连接。 Parallel.ForEach has overloads to make this easy具有在线程开始和结束时运行的函数。
var numbers = new List<avl_range>();
Func<NpgsqlConnection> localInit => () =>
{
var conn = new NpgsqlConnection(strConnection);
conn.Open();
};
Action<NpgsqlConnection> localFinally = (conn) => conn.Dispose();
Func<avl_range, ParallelLoopState, NpgsqlConnection, NpgsqlConnection> 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);
话虽如此,大部分时间与数据库并发连接都不是正确的想法,瓶颈可能在其他地方,你应该使用分析器来查看什么真正减慢了你的程序,并集中精力在那里。 / p>
评论的示例代码:
var numbers = GetDataForNumbers();
List<string> results = new List<string>();
Func<List<string>> localInit => () => new List<string>();
Func<avl_range, ParallelLoopState, List<string>, List<string>> 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<List<String>> 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.