Can I use Parallel.For with sql Commands?

前端 未结 2 1937
别那么骄傲
别那么骄傲 2020-12-17 05:25

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

2条回答
  •  春和景丽
    2020-12-17 06:19

    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. Parallel.ForEach has overloads to make this easy which have functions that run at the start of a thread and at the end.

    var numbers = new List();
    
    Func localInit => () => 
    {
        var conn = new NpgsqlConnection(strConnection);
        conn.Open();
    };
    
    Action localFinally = (conn) => conn.Dispose();
    
    Func 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 results = new List();
    
    Func> localInit => () => new List();
    
    Func, List> 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> 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.
    

提交回复
热议问题