简体   繁体   English

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

[英]Can I use Parallel.For with sql Commands?

I have a class of range 我有一类范围

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

If I use a normal FOR works perfect, but have to wait for each command to finish and each query take 8 seconds, so 10 queries take 80 seconds. 如果我使用正常的FOR工作完美,但必须等待每个命令完成并且每个查询需要8秒,所以10个查询需要80秒。

In the Parallel version If I only print the ranges works perfect, but if try to execute the command say is already in progress. 在Parallel版本中如果我只打印范围工作完美,但如果尝试执行命令说已经在进行中。

{"An operation is already in progress."} {“一项操作已在进行中。”}

How can I solve this? 我怎么解决这个问题?

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);
    }
 );

FYI: Im trying to solve this issue I post it before 仅供参考:我试图解决这个问题,我之前发布

An Npgsql connection can't be used concurrently - only one command may be running at any given point in time (in other words, no MARS support). 不能同时使用Npgsql连接 - 在任何给定的时间点只能运行一个命令(换句话说,没有MARS支持)。

It may definitely make sense to open multiple connections to perform your queries in parallel. 打开多个连接以并行执行查询肯定是有意义的。 Although establishing a new physical connection is expensive, connection pooling is extremely lightweight, so there's very little overhead in reusing physical connections. 尽管建立新的物理连接非常昂贵,但连接池非常轻量级,因此重用物理连接的开销非常小。 The main reason not to do this is if you need your multiple operations to be in the same transaction. 不这样做的主要原因是,如果您需要多个操作在同一个事务中。

Even if you could get it to work with MARS, connection objects are almost never thread safe anyway, you need to have a connection per thread. 即使您可以使用MARS,连接对象几乎从不是线程安全的,您需要为每个线程建立连接。 Parallel.ForEach has overloads to make this easy which have functions that run at the start of a thread and at the end. Parallel.ForEach有重载使得这很容易 ,它具有在线程开始和结束时运行的函数。

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);

That being said, most of the time doing concurrent connections to a database is not the right idea, the bottleneck is likely elsewhere and you should use a profiler to see what is really slowing your program down and focus your efforts there. 话虽这么说,大部分时间与数据库并发连接都不是正确的想法,瓶颈可能在其他地方,你应该使用分析器来查看什么真正减慢你的程序,并集中你的努力。


Sample code for comments: 评论示例代码:

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.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM