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

时间:2017-01-27 19:55:30

标签: c# sql postgresql npgsql parallel.for

我有一类范围

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

2 个答案:

答案 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.