【问题标题】:Asynchronous method containing Stored Procedure包含存储过程的异步方法
【发布时间】:2020-01-09 13:24:52
【问题描述】:

在我的控制器中,[POST] 方法通常从数据库中调用存储过程。我想知道我是否在做正确的事情以避免并发问题。以如下方法为例:

    [HttpPost]
    public async Task<IActionResult> Create(Phase phase)
    {
        int releaseId = (int)TempData["id"];
        string connectionString = Configuration["ConnectionStrings:DefaultConnection"];
        using (SqlConnection connection = new SqlConnection(connectionString))
        {
            string sql = "CreatePhase";

            using (SqlCommand command = new SqlCommand(sql, connection))
            {
                command.CommandType = CommandType.StoredProcedure;

                // adding parameters
                SqlParameter parameter = new SqlParameter
                {
                    ParameterName = "@Name",
                    Value = phase.Name,
                    SqlDbType = SqlDbType.VarChar,
                    Size = 50
                };
                command.Parameters.Add(parameter);

                parameter = new SqlParameter
                {
                    ParameterName = "@ReleaseId",
                    Value = releaseId,
                    SqlDbType = SqlDbType.Int
                };
                command.Parameters.Add(parameter);

                connection.Open();
                await command.ExecuteNonQueryAsync();
                connection.Close();
            }
        }

        return RedirectToAction("Index", "Phase", new { id = releaseId });
    }

await command.ExecuteNonQueryAsync(); 是避免并发的正确方法吗?它有什么作用吗?有没有更好的方法来实现这一点,或者这是否足够好?

【问题讨论】:

  • 附注:当你使用'using'语句时,你不必手动关闭连接,'using'语句无论如何都会关闭它
  • 不,await command.ExecuteNonQueryAsync() 与并发问题无关。也不清楚为什么要避免并发,通常人们努力实现它。
  • @GSerg 那么它对我有什么帮助吗?如何处理并发问题?
  • 你所说的并发问题是什么意思?
  • 例如 - 2 个用户想要同时更新 Phase(虽然在原始帖子中我使用了 Create() 方法)。通过“并发问题”,我理解如果两个用户都想同时更新,它不会允许他们。

标签: c# asp.net-core concurrency


【解决方案1】:

异步与并发无关。事实上,如果有的话,它会增加并发问题,因为异步操作没有定义的顺序,因此是“异步的”。但是,在像 Web 应用这样的多线程环境中,并发是一个问题,无论是同步还是异步,因为大约 1000 个线程中的每一个都可以同时执行相同的工作。

处理并发只有两种真正的方法:锁和并发令牌。并发令牌是最好的方法,但它们仅适用于现有的事物(而不是插入)。基本上,您有一列存储并发令牌,并且每次对该行进行操作时,都会更新令牌。然后,在您实际执行操作之前,您检查令牌是否与您最初获得该行时的令牌相同(意味着没有任何变化)。如果是这样,那么您可以继续操作。如果不是,那么其他东西已经对其进行了修改,因此继续进行下去不再安全。此检查是通过 where 子句完成的。例如:

update Foos set Bar = 'Baz' where Id = 1 and Version = {token}

如果Version 中的令牌不再相同,那么显然没有行会匹配并且不会更新任何内容。然后,您可以使用返回的操作计数(即零)来确定存在并发故障并恢复。

这不适用于插入,因为没有什么可以确定是否发生更改。 (您不能在插入中使用 where 子句。)对于这种情况,您别无选择,只能使用锁来防止在执行操作时发生其他任何事情。但是,锁绝对会扼杀您的吞吐量,因为它本质上会强制后续请求排队,并且一次只能处理一个。因此,当并发​​成为问题时,您应该完全避免这些类型的操作。 (不相互冲突的插入是可以的。)

【讨论】:

  • 感谢您的回答!不过,我有一个小问题 - 对于用户一次执行相同操作的可能性很小的小型系统,我认为如果我不采取措施防止并发性就可以了,因为在这种情况下,吞吐量更重要。另外,序列化存储过程会有所帮助吗?
  • 永远不要假设并发不会成为问题。即使是拥有大量用户的网站,像这样的操作通常也只需要几毫秒。然而,在此期间仍然可能发生并发冲突。一切都与时机有关。你的网站只能有两个活跃用户,但如果这两个活跃用户恰好同时做同样的事情,你就完蛋了。如果您知道存在并发冲突的可能性,那么您需要为此做好计划,并有办法恢复。
【解决方案2】:

首先,异步不是并发或多线程,而是提高吞吐量。您是否正在尝试解决并发性或吞吐量问题?吞吐量意味着许多请求调用该过程而无需等待另一个完成。并发意味着让多个工作人员(线程)同时运行以完成一个进程。查看您的示例,您不需要多个线程,因为该进程不受处理器限制,但 I/O 限制,因此单线程异步操作就可以了

如果您希望您的代码是异步的,请使用“await command.ExecuteNonQueryAsync();”正确,因为您正在执行 I/O 操作,并且在 I/O 操作完成之前它不会保留线程。一旦它点击“等待命令。ExecuteNonQueryAsync();”它会将线程释放给调用者,一旦操作完成,它会返回控制之前执行的线程。

请在这里找到一个很好的答案,

What is the difference between asynchronous programming and multithreading?和一篇优秀的文章https://exceptionnotfound.net/async-await-in-asp-net-csharp-ultimate-guide/

【讨论】:

  • 感谢您的回答!对 - 所以这些异步方法不会解决并发问题。但是会怎样?
  • 你是在解决并发还是吞吐量?吞吐量意味着许多请求调用该过程而无需等待另一个完成。并发意味着让多个工作人员(线程)同时运行以完成一个进程。看看你的例子,你不需要多个线程,因为进程不受处理器限制,但 I/O 有限制,所以单线程异步操作就可以了
  • 太棒了!所以并发应该不是问题。然后,据我了解,吞吐量部分由await?
  • 是的,await 会解决持有线程的问题,从而提高吞吐量
猜你喜欢
  • 2020-01-15
  • 2010-09-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-14
  • 1970-01-01
  • 2019-04-04
相关资源
最近更新 更多