多个用户同时按下按钮,导致C#无限循环失效

问题描述 投票:0回答:1

我在 WinForm 项目上有一个 C# 无限循环代码,它使用 SQL 中的一个值,该值在 1/0 之间交替,如果它是 0,它将继续该过程,而如果它是 1,它将留在循环中。这段代码的目的是为了防止多个人处理X。

我的问题是,如果我有多个用户使用该程序,并且其中一些用户同时按下按钮来运行该进程,则循环无法捕获已经有人在处理 X。

我认为问题是所有用户都在读取 SQL 值,然后他们才有机会将 SQL 中的值更新为 1,并阻止其他用户继续该过程。

有办法解决这个问题吗?

这是循环的示例代码:

SqlDataReader VerifyIfBusyReader = null;
                
                SqlCommand VerifyIfBusy = new SqlCommand("SELECT [Value] FROM [Variables] WHERE [TYPE] = 'IsBusy'", CONNECTION);
                int verify = 1;
                int counts = 0;
                int milliseconds = 2000;
                while (verify == 1)
                {
                    counts++;
                    if (counts == 30)
                    {
                        break;
                    }

                    VerifyIfBusyReader = VerifyIfBusy.ExecuteReader();
                    VerifyIfBusyReader.Read();
                    if (int.Parse(VerifyIfBusyReader[0].ToString()) == 1)
                    {
                        VerifyIfBusyReader.Close();
                    }
                    else if (int.Parse(VerifyIfBusyReader[0].ToString()) == 0)
                    {
                        verify = 0;
                        VerifyIfBusyReader.Close();
                    }
                    
                    Thread.Sleep(milliseconds);
                }

                SqlCommand IsBusyNow = new SqlCommand("UPDATE [Variables] SET [VALUE] = 1 WHERE [TYPE] = 'IsBusy'", CONNECTION);
                VerifyIfBusyReader = IsBusyNow.ExecuteReader();
                VerifyIfBusyReader.Close();

如有任何帮助,我们将不胜感激。

当然,我还有另一个函数,可以在 SQL 完成后将其更新回 0,但这不是这里的问题。

c# winforms sql-server-2008 infinite-loop
1个回答
0
投票

在获取不忙结果和设置它之间存在竞争条件。

您可以将

UPDATE
OUTPUT
一起使用,这将是原子的。循环直到得到结果。

请注意,如果您不应该获得结果集,那么您应该使用

ExecuteNonQuery
,对于单个结果,您可以使用
ExecuteScalar

  • 尽可能使用
    async
  • 确保用
    using
    进行处置。
  • 不需要存储结果和循环,可以立即中断循环。
  • 使用正确的
    for
    循环,而不是
    while
using (var conn = new SqlConnection(ConnStringHere))
{
    using SqlCommand SetBusyOrNull = new SqlCommand(@"
UPDATE Variables
SET VALUE = 1
OUTPUT inserted.VALUE
WHERE TYPE = 'IsBusy'
  AND VALUE = 0;
", conn);

    int counts = 30;
    int milliseconds = 2000;
    for (var i = 0; i < counts; i++)
    {
        await conn.OpenAsync();
        var isSet = (await VerifyIfBusy.ExecuteScalarAsync()) as int? == 1;
        await conn.CloseAsync();
        if (isSet)
            return true;

        await Task.Delay(milliseconds);
    }
    throw new Exception($"Still busy after {counts} tries");
    // alternatively return false;
}
© www.soinside.com 2019 - 2024. All rights reserved.