【问题标题】:Asynchronous waiting while C# function is executingC# 函数执行时的异步等待
【发布时间】:2009-11-25 03:11:32
【问题描述】:

我有一个阻塞函数,它执行异步 MySQL 查询并在获得结果时返回结果。原因是异步的,这个程序不允许在查询期间锁定。

当用户按下按钮时会调用该函数,因此在第一个查询完成之前该函数可能会被调用多次。我想我可以添加一个布尔值来检查查询是否正在执行,并让函数等到它完成后再继续,但它没有按预期工作。我使用的两个 DoEvents() 存在一些问题。如果你注释掉其中任何一个,它运行得很好,除了 UI 冻结。

如何使函数在执行查询时进行非阻塞等待,以及在获取查询本身时进行非阻塞等待?我真的更愿意将它保留在一个线程上,因为函数本身会阻塞调用它的代码。任何帮助将不胜感激!

    public Exception LastError;
    public MySqlConnection Conn;
    public MySqlDataReader Reader;
    public bool IsExecuting = false;

    public MySqlDataReader MySQL_Query(string Query, [Optional] params string[] Values)
    {
        while (IsExecuting)
        {
            System.Windows.Forms.Application.DoEvents();
            System.Threading.Thread.Sleep(20);
        }

        if (IsConnected() == false)
            ConnectToDatabase();

        for (int i = 0; i < Values.Length; i++)
            Values[i] = MySQL_SafeValue(Values[i]);
        if (Reader != null && Reader.IsClosed == false)
            Reader.Close();

        IsExecuting = true;
        try
        {
            MySqlCommand Cmd = new MySqlCommand(String.Format(Query, Values), Conn);
            IAsyncResult aRes = Cmd.BeginExecuteReader();
            while (!aRes.IsCompleted)
            {
                System.Windows.Forms.Application.DoEvents();
                System.Threading.Thread.Sleep(20);
            }
            Reader = Cmd.EndExecuteReader(aRes);
            IsExecuting = false;
        }
        catch (Exception e)
        {
            IsExecuting = false;
            LastError = e;
            return null;
        }

        return Reader;
    }

【问题讨论】:

    标签: c# mysql function asynchronous blocking


    【解决方案1】:

    您不应该使用DoEventsSleep 来创建响应式用户界面。如需在 UI 中执行异步操作,请参阅 BackgroundWorker 类。

    【讨论】:

      【解决方案2】:

      有很多方法可以进行异步工作,从直接使用线程池到像 BackgroundWorker 这样的帮助器。

      但是,这并不能回答您有点矛盾的主要问题,即您想要进行非阻塞等待。我建议您根本不要阻止,也就是说,如果您已经在执行,那么忽略该请求并且什么都不做。在这种情况下,您可能想提供一些反馈,说“已经工作”。

      现在谈谈您的代码的实际问题。正如亚当所指出的那样,您真的不应该使用 DoEvents 和 Sleep。相反,您将长时间运行的工作项发布到某个后台任务并使用标志在 UI 线程和运行您的任务的线程之间进行同步,例如

          /// <summary>
          /// Used to prevent more than one worker.
          /// </summary>
          private bool working = false;
      
          /// <summary>
          /// Must use a lock to synch between UI thread and worker thread.
          /// </summary>
          private object stateLock = new object();
      
          /// <summary>
          /// Used to pass custom args into the worker function.
          /// </summary>
          private class Data
          {
              public string query;
              public string[] values;
          }
      
          /// <summary>
          /// Called in your UI thread in response to button press.
          /// </summary>
          /// <param name="Query"></param>
          /// <param name="Values"></param>
          public void UiRequestToDoWork(string Query, params string[] Values)
          {
              lock (stateLock)
              {
                  if (working)
                  {
                      // Do nothing!
                      Trace.WriteLine("Already working!");
                  }
                  else
                  {
                      var backgroundWorker = new System.ComponentModel.BackgroundWorker();
                      backgroundWorker.DoWork += new System.ComponentModel.DoWorkEventHandler(backgroundWorker_DoWork);
                      backgroundWorker.RunWorkerAsync(new Data { query = Query, values = Values });
                      this.working = true;
                  }
              }
          }
      
          /// <summary>
          /// Does all the background work.
          /// </summary>
          /// <param name="sender"></param>
          /// <param name="e"></param>
          void backgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
          {
              try
              {
                  Data data = e.Argument as Data;
                  if (data != null)
                  {
                      // Do your query in here - just simulating work with a sleep.
                      Trace.WriteLine("Working...");
                      System.Threading.Thread.Sleep(500);
      
                      // Note: you can't access the UI directly here in the worker thread. Use
                      // Form.Invoke() instead to update the UI after your work is done.
                  }
              }
              finally
              {
                  // Note the use of finally to be safe if exceptions get thrown.
                  lock (stateLock)
                  {
                      this.working = false;
                  }
                  Trace.WriteLine("Finished!");
              }
          }
      

      【讨论】:

        【解决方案3】:

        尽管当您提出问题时,这不是一个选项,但如果您可以升级到 .NET 4.5,那么现在有一种更简洁的方式来进行异步操作,同时基本上仍以与同步代码相同的方式编写。这涉及使用新的 asyncawait 关键字。

        请参阅:
        An Async Primer 了解新功能的介绍
        here 是一个专门针对 MySQL 连接的 SO 问题。

        【讨论】:

          猜你喜欢
          • 2019-12-30
          • 2017-12-30
          • 1970-01-01
          • 1970-01-01
          • 2023-01-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多