【问题标题】:How to use Asynch in C# correctly?如何在 C# 中正确使用异步?
【发布时间】:2021-06-12 20:35:47
【问题描述】:

我有一个来自供应商的演示应用程序。这个演示应用程序允许我通过网络连接连接到 RFID 阅读器。一旦建立连接,我就可以扫描 RFID 标签,然后标签数据会显示在应用程序上。

每当 CALienServer 收到来自 RFID 阅读器的消息时,都会引发事件处理程序。

private void addServerEvents(int idx)
{
    mServers[idx].ServerMessageReceived += new CAlienServer.ServerMessageReceivedEventHandler(mServer_ServerMessageReceived);
    mServers[idx].ServerConnectionEstablished += new CAlienServer.ServerConnectionEstablishedEventHandler(mServer_ServerConnectionEstablished);
    mServers[idx].ServerConnectionEnded += new CAlienServer.ServerConnectionEndedEventHandler(mServer_ServerConnectionEnded);
    mServers[idx].ServerSocketError += new CAlienServer.ServerSocketErrorEventHandler(mServer_ServerSocketError);
}

private void mServer_ServerMessageReceived(string msg)
{
    if (msg != null)
    {
        displayText(msg.Insert(msg.IndexOf(" ") + 1, "\r\n"));
    }    
}

private void displayText(String data)
{
    try
    {
        if (this.InvokeRequired)
        {
            object[] temp = {data};
            this.Invoke(new displayMessageDlgt(displayText),temp);
            return;
        }
        else
        {
            txtNotifications.Text += data.Replace("\0","") + "\r\n";
            Task task = insert(data);
            NotifyInfo ni = null;
            AlienUtils.ParseNotification(data, out ni);
            if (ni != null)
            {
                lblReaderName.Text = ni.ReaderName;
            }
        }
    }
    catch(Exception ex)
    {
        Debug.WriteLine("Exception in the DiscplayText(): " + ex.Message);
    }
}

我想知道运行另一个函数,将变量“data”或“msg”中的数据存储到数据库中。我知道调用同步函数将不起作用,因为如果编程正在运行将数据插入数据库函数并且阅读器拾取新标签怎么办。因此,同步功能将不起作用。我正在考虑使用异步函数。所以我的数据库插入功能是。这是正确的做法吗?

private async Task insert(string rfid)
{
   await Task.Factory.StartNew(()=>
   {
      using (var connection = new MySqlConnection("server=localhost;user id='" + username_mysql + "';database=new;password='" + password_mysql + "'"))
      {
        connection.Open();

        try
        {
           string sql = "INSERT INTO `data` (`RFID TAG`) VALUE (@rfid)";

           MySqlCommand cmd = new MySqlCommand(sql, connection);

           cmd.Parameters.Add("@rfid", MySqlDbType.VarChar).Value = rfid;

           if (cmd.ExecuteNonQuery() == 1)
           {
             

           }
        }
        catch (Exception ex)
        {
            
        }
    }
   });
}

错误 1:

更新:

代码:

    private void insert(string rfid)
    {
        using (var connection = new MySqlConnection("server=localhost;user id='" + username_mysql + "';database=alien;password='" + password_mysql + "'"))
        {
            connection.Open();

            try
            {
                string sql = "INSERT INTO `data` (`DATA`) VALUE (@data)";

                MySqlCommand cmd = new MySqlCommand(sql, connection);

                cmd.Parameters.Add("@data", MySqlDbType.VarChar).Value = rfid;

                if (cmd.ExecuteNonQuery() == 1)
                {
                    Debug.WriteLine("Successfull: " + rfid);
                    textBox2.BackColor = Color.Green;

                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Exception in the insert(): " + ex.Message);
            }
        }
    }

private void displayText(String data)
{
    try
    {
            if (this.InvokeRequired)
            {
                object[] temp = {data};
                this.Invoke(new displayMessageDlgt(displayText), temp);
                return;
            }
            else
            {
                txtNotifications.Text += data.Replace("\0","") + "\r\n";
                //await Task.Run(() => insert(data));
                insert(data);
                textBox2.BackColor = Color.Red;

                NotifyInfo ni = null;
                AlienUtils.ParseNotification(data, out ni);
                if (ni != null)
                {
                    lblReaderName.Text = ni.ReaderName;
                }
            }
        }
        catch(Exception ex)
        {
            Debug.WriteLine("Exception in the DisplayText(): " + ex.Message);
        }
  }

SERVER MESSAGE 来自 mServer_ServerMessageReceived(string msg) 方法 成功来自于 insert() 方法。

这清楚地表明所有标签数据都已插入数据库中。

调试输出:

服务器消息:00:1B:5F:00:F9:B4 0004,3,1

服务器消息:00:1B:5F:00:F9:B4 A123 0001,3,1

成功:00:1B:5F:00:F9:B4 0004,3,1

服务器消息:00:1B:5F:00:F9:B4 A641 2541,3,1

成功:00:1B:5F:00:F9:B4 A123 0001,3,1

服务器消息:00:1B:5F:00:F9:B4 0014,3,1

成功:00:1B:5F:00:F9:B4 A641 2541,3,1

服务器消息:00:1B:5F:00:F9:B4 5253 7AB2 0200 0000 0000 1002,3,1

成功:00:1B:5F:00:F9:B4 0014,3,1

服务器消息:00:1B:5F:00:F9:B4 A064 0001 0601,3,1

成功:00:1B:5F:00:F9:B4 5253 7AB2 0200 0000 0000 1002,3,1

服务器消息:00:1B:5F:00:F9:B4 0000 0000,3,1

服务器消息:00:1B:5F:00:F9:B4 0001,3,1

服务器消息:00:1B:5F:00:F9:B4 2123 6521,3,1

服务器消息:00:1B:5F:00:F9:B4 0016,3,1

成功:00:1B:5F:00:F9:B4 A064 0001 0601,3,1

成功:00:1B:5F:00:F9:B4 0000 0000,3,1

成功:00:1B:5F:00:F9:B4 0001,3,1

成功:00:1B:5F:00:F9:B4 2123 6521,3,1

成功:00:1B:5F:00:F9:B4 0016,3,1

【问题讨论】:

  • 不,这不是使用异步的正确方式,原因有很多,包括use of StartNewfire and forget,以及“同步函数不起作用”的原因。
  • 我知道调用同步函数是行不通的,因为如果程序正在运行将数据插入数据库函数并且阅读器拾取新标签怎么办。 听起来像您担心并发性。这与async 无关。
  • 删除Task.Factory.StartNew。将 cmd.ExecuteNonQuery 更改为 await cmd.ExecuteNonQueryAsync。如果您有实际的并发问题,请发布不同的问题并提供详细信息。
  • 附带说明,根据official guidelines,C# 中的方法名称遵循 PascalCase 模式。异步方法也应该有Async 后缀。所以它是InsertAsync,而不是insert
  • @Paul 实施您的代码并进行测试,看看您是否可以重现您提到的那种问题(“运行将数据插入数据库函数并且阅读器拾取新标签”)

标签: c# mysql visual-studio asynchronous


【解决方案1】:

您应该改用ExecuteNonQueryAsync。请注意,这可能会或可能不会实际执行异步。

对于 Oracles MySql 连接器,所有异步方法 execute synchronouslyMySqlConnector 声称提供真正的异步支持,但我没有对此进行测试。 (感谢 Bradley Grainger 提供更多信息。)

您是否确定查询实际上是同步运行的,在后台线程上运行查询以避免阻塞 UI(如果它是 UI 应用程序)可能是合理的。如果是这样,最好先创建一个同步方法。

private void insert(string rfid){
    ...
}

然后这样称呼它

private async Task displayText(String data){
    ....
    await Task.Run(() => insert(rfid));
    ...
}

如果你这样做,你需要保证该方法是线程安全的。如果你不能保证这一点,你不应该在另一个线程上运行它。请注意,您应该确保正确处理异常。即

  • 避免空捕获,所有异常都应该以某种方式处理。如果您无法插入所需的数据,则可能应该通知用户。
  • 应等待所有任务,否则将丢失任何异常。
  • 避免使用async void,因为这会使调用方无法等待,因此无法接收任何异常。
  • 对于事件处理程序(即按下按钮),您需要使用async void,在这种情况下,您应确保捕获任何异常并采取适当的措施。可能会通知用户失败,和/或终止应用程序。

【讨论】:

  • 使 displayText() 成为异步方法而不是 insert() 有什么区别
  • 请注意,使用 MySql.Data,所有“异步”方法实际上都是同步执行的:bugs.mysql.com/bug.php?id=70111 在 .NET 中使用 MySQL 获得真正异步 I/O 的唯一方法是切换到不同的 ADO。 NET 库:mysqlconnector.net/tutorials/migrating-from-connector-net
  • @Paul,每当您调用异步方法时,您都需要处理结果,即使这只是一个执行,这很容易通过等待来完成。因此调用者也需要是async Task。这种情况一直持续到可以适当处理异常的程度,可能在 UI 层中。
  • @Bradley Grainger 感谢您提供的信息,并将其添加到答案中。我很怀疑,但没有参考资料。
  • @Paul 有很多指南和最佳实践描述了您应该如何处理异常。 Microsofts programming guide about exceptions 可能是一个好的开始。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多