C#函数执行时异步等待

时间:2009-11-25 03:11:33

标签: c# mysql function asynchronous blocking

我有一个阻塞函数,它执行异步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;
    }

3 个答案:

答案 0 :(得分:6)

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

答案 1 :(得分:3)

有许多方法可以执行异步工作,从直接使用线程池到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!");
        }
    }

答案 2 :(得分:1)

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

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