我在asp.net .asmx服务中有一个webmethod,它应该检查数据库中是否有记录,如果没有记录则应该添加记录
代码的简化示例如下:
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语句本身中实现它的原因,但实质上就是它。
大多数情况下代码运行良好,但有时我因为多线程而遇到竞争条件,尽管我使用了锁。
我今天得到的示例输出:
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会以这样的方式运行?在没有竞争条件的情况下完成该任务的正确方法是什么?
答案 0 :(得分:3)
您发生锁定的对象需要是静态的,否则每个进入您服务的请求都会有一个实例。