【问题标题】:Connection pool – one process – many threads连接池——一个进程——多线程
【发布时间】:2014-02-21 20:14:18
【问题描述】:

我对连接池的理解是;如果连接字符串完全相同,那么我们重用连接而不是建立新连接。

我的问题是我为并行处理创建了许多线程。在这个“虚拟”程序中,我创建了 500 个线程并让 ThreadPool 函数处理这些线程。

步骤如下:

  1. 每个线程在 SQL 中创建一个更新表。 (说明更新的时间戳)

  2. 然后线程休眠 1 到 10 秒(随机)。

  3. 最后,线程在 SQL 中进行另一次更新(说明结束时间的时间戳)

  4. 然后线程退出

    class Program
    {
        static void Main(string[] args)
        {
            int numberOfThreads = 150;
    
            ThreadPool.SetMinThreads(numberOfThreads, numberOfThreads);
            ThreadPool.SetMaxThreads(numberOfThreads, numberOfThreads);
    
            List<Int64> chunkList = new List<Int64>();
    
            int maxNumberOfChunks = 500;
            for (int i = 1; i < maxNumberOfChunks; i++)
            {
                chunkList.Add(i);
            }
    
            foreach (Int64 chunk_id in chunkList)
            {
                ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadWorker), new arguments { chunk_id = chunk_id });
            }
            Console.ReadLine();
    
        }
        static void ThreadWorker(Object stateInfo)
        {
            arguments arguments = (arguments)stateInfo;
    
            Console.WriteLine("Chunk # : {0} is set to START", arguments.chunk_id);
            UpdateSQLdb(arguments.chunk_id, DateTime.Now, null, null, "START", null, null);
    
            Random random = new Random();
            int mseconds = random.Next(1, 10) * 1000;
            System.Threading.Thread.Sleep(mseconds);
            Console.WriteLine("Chunk # : {0} is sleeping for {1} sec.", arguments.chunk_id, mseconds);
    
            Console.WriteLine("Chunk # : {0} ist set to END", arguments.chunk_id);
            UpdateSQLdb(arguments.chunk_id, null, DateTime.Now, null, "END", null, null);
        }
        struct arguments
        {
            public Int64 chunk_id;
        }
    
        static void UpdateSQLdb(Int64 CHUNK_ID, DateTime? START_TS = null, DateTime? END_TS = null, Enum CHUNK_STATUS = null, string error_messages = null, byte? NEW_CALCULATION_ATTEMPTS = null, byte? NEW_POSTPROCESS_ATTEMPTS = null)
        {
            using (SqlConnection conn = new SqlConnection("Data Source=C55S01;Initial Catalog=MCS_BATCH;Integrated Security=SSPI;Asynchronous Processing=True")) //Timeout=60;Max Pool Size=200;Pooling=True;
            {
                int result = -1;
                conn.Open(); //<-- Each time I open a connection. It creates a new instead of reusing one from the ConnectionPool
    
                try
                {
                    using (SqlCommand cmd = new SqlCommand("TEST.UpdateSQL", conn))
                    {
                        cmd.CommandTimeout = 300; 
                        cmd.CommandType = System.Data.CommandType.StoredProcedure;
    
                        cmd.Parameters.Add("@CHUNK_ID", SqlDbType.BigInt, 15).Value = CHUNK_ID;
                        cmd.Parameters.Add("@START_TS", SqlDbType.DateTime2, 7).Value = START_TS;
                        cmd.Parameters.Add("@END_TS", SqlDbType.DateTime2, 7).Value = END_TS;
                        cmd.Parameters.Add("@ERR_MESSAGE", SqlDbType.VarChar).Value = error_messages;
                        cmd.Parameters.Add("@ReturnValue", System.Data.SqlDbType.Int, 4).Direction = System.Data.ParameterDirection.ReturnValue;
    
                        try
                        {
                            result = cmd.ExecuteNonQuery();
    
                            int return_value = (int)cmd.Parameters["@ReturnValue"].Value;
                            if (return_value != 0)
                            {
                                Console.WriteLine("1. Error in running TEST.UpdateSQL, return value is : {0}", cmd.Parameters["@ReturnValue"].Value);
                            }
                        }
                        catch (SqlException ex)
                        {
                            UpdateSQLdb(CHUNK_ID, null, DateTime.Now, null, ex.Message.ToString(), null, null);
                            Console.WriteLine("2. Error executing TEST.UpdateSQL : {0}", ex);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("3.Error in TEST.UpdateSQL : {0}", ex);
                    throw;
                }
                if (conn.State == ConnectionState.Open)
                {
                    Console.WriteLine("Closing connection....");
                    conn.Close();
                }
                conn.Dispose();
            }
        }
    }
    

    }

我的问题是我得到了一个 System.InvalidOperationException 未处理 (超时。在从池中获取连接之前已经过了超时时间。这可能是因为所有池连接都在使用中,并且达到了最大池大小。)

我已经监控了 SQL 服务器上的连接数,很快就达到了 100 个连接(这是一个池中默认的最大连接数)

如果尝试将这些参数添加到连接字符串: Timeout=60"Max Pool Size=200;Pooling=True;

但这只是将问题推迟到稍后阶段,因为连接池将达到 200 并且超时将在某个时候达到。

问题:为什么要一遍又一遍地创建连接,而不是重复使用连接池中的一个?

非常感谢任何提示、提示或建议。

【问题讨论】:

  • 您正尝试在 100 个连接上运行 500 个并发进程。你认为这怎么可能? SQL 连接池不能在同一连接上同时运行两个(或三个或四个)不同的命令。这不是连接池的意思。
  • 无论如何,您的网络连接实际上不太可能有能力跟上那么多并发请求。你几乎肯定不应该有那么多线程或那么多连接,除非这是一个有大量资源支持的大型服务器。
  • 这是个好问题。您可以在使用后安全地处理连接。您对连接对象的使用仅在单个呼叫期间使用。池应该可以正常工作。从池中获取连接会超时。如果没有可用的连接,它不会明显失败。如果所有连接都保持一小段时间,这应该可以完美地工作。调查,为什么连接会保持很长时间。
  • 无关:您正在使用迷信的处置模式。处理 3 次而不是 1 次没有任何好处。

标签: c# sql-server multithreading connection-pooling invalidoperationexception


【解决方案1】:

它正在做你要求它做的事情。它正在使用池中的连接,但您给它做了太多工作。如果您有 500 个线程和 200 个连接,则每个线程都不能有连接。您可能应该拥有与线程一样多的连接。

如果您还有更多工作要做(所有 500 个线程都忙),那么您必须向使用者返回错误或以其他方式限制应用程序的输入。

【讨论】:

    猜你喜欢
    • 2019-07-07
    • 2018-08-13
    • 1970-01-01
    • 2018-12-04
    • 1970-01-01
    • 1970-01-01
    • 2019-12-04
    • 2022-11-02
    • 2023-03-12
    相关资源
    最近更新 更多