C# 中的对象池抛出 StackoverFlow 异常
Object pooling in C# throwing StackoverFlow Exception
我正在尝试在 C# 中实现对象池。我的要求是有一个可以容纳 100 个活动 SqlConnection 对象的池。如果池已经有 100 个连接并且如果用户请求一个新连接,则池必须等到一个现有连接被释放。
下面是我使用的代码。在池中使用对象达到 100 后,我收到 Whosebug 异常。
请提出以下代码中 Whosebug 异常的可能原因。
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);
}
}
}
当您达到最大值 100 时,您正在递归调用 GetConnection
。由于您并行执行 150000,一旦您使用了 100(在这种情况下很快就会发生),您将开始递归调用并最终命中 WhosebugException
,因为递归调用发生的速度比释放连接快得多,并且发生的调用比可用连接多。
如果期望的行为是等待一定时间并重试,您将需要重构 GetConnection
调用以使其处于无限循环中,不断调用相同的代码直到它获得连接.
你也可以考虑加一个超时值,如果在一定时间内连接不上,就抛出异常。
最后,如果您认为有可能有可用的连接,您可能只想输入锁定的代码。这会导致对锁进行双重检查,但您输入锁定代码的频率会降低。
我正在尝试在 C# 中实现对象池。我的要求是有一个可以容纳 100 个活动 SqlConnection 对象的池。如果池已经有 100 个连接并且如果用户请求一个新连接,则池必须等到一个现有连接被释放。
下面是我使用的代码。在池中使用对象达到 100 后,我收到 Whosebug 异常。
请提出以下代码中 Whosebug 异常的可能原因。
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);
}
}
}
当您达到最大值 100 时,您正在递归调用 GetConnection
。由于您并行执行 150000,一旦您使用了 100(在这种情况下很快就会发生),您将开始递归调用并最终命中 WhosebugException
,因为递归调用发生的速度比释放连接快得多,并且发生的调用比可用连接多。
如果期望的行为是等待一定时间并重试,您将需要重构 GetConnection
调用以使其处于无限循环中,不断调用相同的代码直到它获得连接.
你也可以考虑加一个超时值,如果在一定时间内连接不上,就抛出异常。
最后,如果您认为有可能有可用的连接,您可能只想输入锁定的代码。这会导致对锁进行双重检查,但您输入锁定代码的频率会降低。