我在asp.net .asmx服务中有一个add方法,wich应该检查数据库中是否有记录,如果没有记录,它应该添加记录。
简化的代码示例如下:
object Mutex = new object();
[WebMethod]
public void InsertIfNotExists(string CLI)
{
lock (Mutex)
{
using (SqlConnection conn = new SqlConnection(ConnectionString))
using (SqlDataAdapter adapter = new SqlDataAdapter())
using (DataSet ds = new System.Data.DataSet()){
{
//I log with log4net
logger.Debug("InsertIfNotExists: Start Function: CLI:" + CLI);
int dummy = 0;
string sql = "SELECT * CLI Promote where CLI=" + CLI + " ";
adapter.SelectCommand = new SqlCommand(sql, conn);
adapter.Fill(ds);
DataTable t = ds.Tables[0];
logger.Debug("InsertIfNotExists: " + t.Rows.Count + " records found for CLI:" + CLI);
if (t.Rows.Count == 0)
{
logger.Debug("InsertIfNotExists: starting to add to table: CLI:" + CLI);
DataRow dr = t.NewRow();
dr["CLI"] = CLI;
dr["DateOfSend"] = DateTime.Now;
InsertToTable(t, dr, sql);
logger.Debug("InsertIfNotExists: added to table: CLI:" + CLI + ", starting re-check");
//checking if exist more then one lines - one more time
sql = "SELECT * CLI Promote where CLI=" + CLI + "";
adapter.SelectCommand = new SqlCommand(sql, conn);
adapter.Fill(ds);
t = ds.Tables[0];
logger.Debug("InsertIfNotExists: re-check for CLI:" + CLI + ", records count=" + t.Rows.Count);
}
logger.Debug("InsertIfNotExists: Finish Function for CLI:" + CLI);
}
}
}实际上,它会执行更多的检查和逻辑,这就是为什么我在.net中实现它,而不是在SQL语句本身中实现它,但本质上就是这样。
大多数情况下,代码运行良好,但有时由于多线程而进入竞争状态,尽管我使用了lock。
我今天得到的样本输出:
2013-09-15 11:47:14,145 [21] DEBUG Namespace.Service1 InsertIfNotExists: Start Function: CLI: 0501234567
2013-09-15 11:47:14,145 [13] DEBUG Namespace.Service1 InsertIfNotExists: Start Function: CLI: 0501234567
2013-09-15 11:47:14,148 [21] DEBUG Namespace.Service1 InsertIfNotExists: 0 records found for CLI: 0501234567
2013-09-15 11:47:14,148 [21] DEBUG Namespace.Service1 InsertIfNotExists: starting to add to table: CLI: 0501234567
2013-09-15 11:47:14,148 [13] DEBUG Namespace.Service1 InsertIfNotExists: 0 records found for CLI: 0501234567
2013-09-15 11:47:14,148 [13] DEBUG Namespace.Service1 InsertIfNotExists: starting to add to table: CLI: 0501234567
2013-09-15 11:47:14,149 [21] DEBUG Namespace.Service1 InsertIfNotExists: added to table: CLI: 0501234567, starting re-check
2013-09-15 11:47:14,149 [13] DEBUG Namespace.Service1 InsertIfNotExists: added to table: CLI: 0501234567, starting re-check
2013-09-15 11:47:14,154 [27] DEBUG Namespace.Service1 InsertIfNotExists: Start Function: CLI: 0501234567
2013-09-15 11:47:14,157 [27] DEBUG Namespace.Service1 InsertIfNotExists: 2 records found for CLI: 0501234567
2013-09-15 11:47:14,157 [27] DEBUG Namespace.Service1 InsertIfNotExists: Finish Function for CLI: 0501234567
2013-09-15 11:47:14,183 [13] DEBUG Namespace.Service1 InsertIfNotExists: re-check for CLI: 0501234567, records count=2
2013-09-15 11:47:14,184 [21] DEBUG Namespace.Service1 InsertIfNotExists: re-check for CLI: 0501234567, records count=2
2013-09-15 11:47:14,185 [13] DEBUG Namespace.Service1 InsertIfNotExists: Finish Function for CLI: 0501234567
2013-09-15 11:49:19,626 [21] DEBUG Namespace.Service1 InsertIfNotExists: Start Function: CLI:0507654321我们在这里看到的是,有3个线程试图在并行的表中插入CLI 0501234567。线程21和13进入竞赛条件,每一个都插入了1项记录。然后线程27也尝试插入一个记录,但找到了现有的记录并退出。
为什么他们锁在互斥锁上就这么做了?
注意:线程21永远无法完成--我认为它是由线程21中的异常引起的,因为我试图在实际函数中重新检查之后删除“附加”行,然后尝试执行它的第二个线程应该进入异常。我知道这很难看,但这是我目前唯一的解决办法,我想知道如何正确地做这件事。
为什么asp.net的行为是这样的?在没有种族条件的情况下,完成该任务的正确方法是什么?
发布于 2013-09-15 19:24:13
锁定发生的对象必须是静态的,否则服务中的每个请求都会有一个该对象的实例。
https://stackoverflow.com/questions/18812429
复制相似问题