1

我在 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 会以这种方式运行,在没有竞争条件的情况下完成该任务的正确方法是什么?

4

1 回答 1

3

发生锁定的对象必须是静态的,否则对于进入服务的每个请求都会有一个实例。

于 2013-09-15T19:24:13.737 回答