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 List

Npgsql连接不能同时使用-在任何给定时间点只能运行一个命令(换句话说,不支持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不支持连接池,这种方式会更快,因为它会创建更少的总连接(单个线程可以在主体的多次迭代中重用)。但是因为谢伊证实了这一点