C# 我可以将Parallel.For与sql命令一起使用吗?
我有一类射程C# 我可以将Parallel.For与sql命令一起使用吗?,c#,sql,postgresql,npgsql,parallel.for,C#,Sql,Postgresql,Npgsql,Parallel.for,我有一类射程 public class avl_range { public long start { get; set; } public long end { get; set; } } 如果我对使用普通的,效果很好,但必须等待每个命令完成,每个查询需要8秒,因此10个查询需要80秒 在并行版本中,如果我只打印范围,效果会很好,但如果尝试执行命令,则表示已在进行中 {“操作已在进行中。”} 我如何解决这个问题? var numbers = new List<avl_r
public class avl_range
{
public long start { get; set; }
public long end { get; set; }
}
如果我对使用普通的,效果很好,但必须等待每个命令完成,每个查询需要8秒,因此10个查询需要80秒
在并行版本中,如果我只打印范围,效果会很好,但如果尝试执行命令,则表示已在进行中
{“操作已在进行中。”}
我如何解决这个问题?
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);
}
);
var numbers=new ListNpgsql连接不能同时使用-在任何给定时间点只能运行一个命令(换句话说,不支持MARS)
打开多个连接以并行执行查询肯定是有意义的。尽管建立一个新的物理连接很昂贵,但连接池非常轻量级,因此重用物理连接的开销非常小。不这样做的主要原因是,如果您需要在同一事务中执行多个操作。即使您可以让它使用MARS,连接对象几乎从来都不是线程安全的,您也需要每个线程都有一个连接。其中的函数在线程开始和结束时运行
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);
var number=新列表();
Func localInit=>()=>
{
var conn=新的NpgsqlConnection(strConnection);
conn.Open();
};
操作localFinally=(conn)=>conn.Dispose();
Func forEachLoop=(number,loopState,conn)=>//开始forLoop的定义
{
//只有控制台写入行工作正常
Console.WriteLine(number.start+“-”+number.end);
使用(var cmd=new NpgsqlCommand())
{
cmd.Connection=conn;
cmd.CommandText=String.Format(“在链接({0},{1})附近的avl_db.process_中选择*)”
,number.start
,编号。结束);
//这里是导致错误的原因。
使用(var reader=cmd.ExecuteReader())
{
while(reader.Read())
{
Console.WriteLine(reader.GetString(0));
}
}
}
返回连接;
};
Parallel.ForEach(number、localnit、forEachLoop、localFinally);
也就是说,大多数情况下并发连接到数据库不是正确的想法,瓶颈可能在其他地方,您应该使用探查器查看是什么真正减慢了您的程序,并将精力集中在那里
注释的示例代码:
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.
var numbers=GetDataForNumbers();
列表结果=新列表();
Func localInit=>()=>新列表();
Func forEachLoop=(number,loopState,localList)=>//开始forLoop的定义
{
使用(var conn=新的NpgsqlConnection(strConnection))
{
conn.Open();
//这一行会让你的程序慢很多,所以我把它注释掉了。
//Console.WriteLine(number.start+“-”+number.end);
使用(var cmd=new NpgsqlCommand())
{
cmd.Connection=conn;
cmd.CommandText=String.Format(“在链接({0},{1})附近的avl_db.process_中选择*)”
,number.start
,编号。结束);
使用(var reader=cmd.ExecuteReader())
{
while(reader.Read())
{
//在线程本地列表中添加一个对象,我们不需要在这里锁定,因为我们是唯一可以访问它的线程。
Add(reader.GetString(0));
}
}
}
}
返回localList;
};
动作localFinally=localList=>
{
//将本地列表与主结果相结合,我们需要在这里锁定,因为一次可以合并多个线程。
锁定(结果)
{
results.AddRange(localList);
}
};
Parallel.ForEach(number、localnit、forEachLoop、localFinally);
//结果现在包含来自此处所有线程的字符串。
在Microsoft的SQL server中,这会导致错误,因为在同一个连接上不能有多个打开的记录集(如果不启用MARS)。我不知道您的SQL提供程序是否以相同的方式实现,但我猜可能是这样。在并行foreach中创建一个新的NpgsqlConnection
怎么样?@BradleyUffner您能提供一些文档吗?我可以尝试,但在我看来,为每个查询创建不同的连接似乎不符合逻辑。这在Microsoft的SQL实现中是可行的。我不能肯定使用postressql,因为我从未使用过它,但值得一试。只要它共享连接,就不应该有太多的性能损失。这似乎表明prostgresql没有与MARS等效的功能。您确定Npgsql实现了连接共享吗?这取决于ADO.NET提供程序是否支持它。老实说,我不知道他们是否知道答案。是的,是我写的:)哦,那好吧:)嗨,谢伊,我是Npgsql论坛上的dropyghost。我只是将连接创建移动到forEachLoop
中,现在正在工作。这样行吗,还是我应该做些别的事情,因为你提到了重用连接?@JuanCarlosOropeza如果你的代码只写控制台,那可能会让你慢下来。控制台是最慢的。使用正常的foreach循环(不带parallel)重试,但将字符串存储在列表中,而不是将其写入控制台,并注释掉console.WriteLine(number.start+“-”+number.end)
然后让我们知道您的时间。谢谢Scott,此设置和将创建连接移动到内部之间有什么区别吗?如果NpgsqlConnection不支持连接池,这种方式会更快,因为它会创建更少的总连接(单个线程可以在主体的多次迭代中重用)。但是因为谢伊证实了这一点