C#中的对象池抛出StackoverFlow异常

时间:2017-01-21 16:26:21

标签: c# multithreading locking stack-overflow object-pooling

我正在尝试在C#中实现对象池。我的要求是拥有一个可以容纳100个活动SqlConnection对象的池。如果池已经有100个连接,并且如果用户请求新连接,则池必须等到一个现有连接被释放。

以下是我使用的代码。在使用对象中池达到100后,我得到了StackOverFlow异常。

请在下面的代码中建议可能是StackOverFlow异常的原因。

class ObjectPoolingTest
{
    static void Main(string[] args)
    {

        int insertedRecords = 1;
        Parallel.For(1, 150000, i =>
        {
            test1(i);
            Console.WriteLine("{0} - Query executed", insertedRecords);
            insertedRecords++;
        }
        );
        Console.ReadKey();

    }

    static void test1(int hitNo)
    {
        var objConnection = Pool.GetConnection();
        SqlCommand cmd = new SqlCommand();
        cmd.Connection = objConnection.ConnectionObject;
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.CommandText = "[dbo].[prSaveRecNumber]";
        cmd.CommandTimeout = 30;
        cmd.Parameters.Add("@recNo", SqlDbType.Int).Value = hitNo;
        int result = cmd.ExecuteNonQuery();
        Pool.ReleaseConnection(objConnection);
        Console.WriteLine(Pool.Message);
    }
}

public class Pool
{
    private static List<Connection> _available = new List<Connection>();
    private static List<Connection> _inUse = new List<Connection>();
    private static int MaxPoolSize = 100;

    private static object lock1 = new Object();
    private static object lock2 = new Object();

    public static string Message
    {
        get
        {
            return string.Format("Available: {0} - InUse: {1}", _available.Count, _inUse.Count);
        }
    }

    public static Connection GetConnection()
    {
        lock (lock1)
        {
            if (_available.Count != 0)
            {
                Connection connection = _available[0];
                _inUse.Add(connection);
                _available.RemoveAt(0);
                return connection;
            }
            else if ((_available.Count + _inUse.Count) != MaxPoolSize)
            {
                Connection connection = new Connection();
                connection.ConnectionObject = new SqlConnection("Server= abcd; Database=sai; User Id=sa; Password=abcd;");

                connection.ConnectionObject.Open();
                _inUse.Add(connection);
                return connection;
            }
            return GetConnection();
        }
    }
    public static void ReleaseConnection(Connection connection)
    {
        lock (lock1)
        {
            _available.Add(connection);
            _inUse.Remove(connection);
        }
    }
}

1 个答案:

答案 0 :(得分:1)

当你达到100的最大值时,你递归地调用GetConnection。因为你并行执行150000,一旦你使用了100(在这种情况下很快发生),你将开始递归调用并最终点击StackOverflowException,因为递归调用发生的速度比释放的连接快得多,并且调用的次数比可用的连接多。

如果所需的行为是等待一段时间并再试一次,则需要重构GetConnection调用以进入无限循环,不断调用相同的代码直到它获得连接。< / p>

您可能还需要考虑添加超时值,如果您在某段时间内无法建立连接,则会抛出异常。

最后,如果您认为有可能存在连接,您可能只想输入锁定的代码。这会导致对锁进行双重检查,但您将不太频繁地输入锁定的代码。