C# 函数执行时异步等待

发布于 2024-08-12 11:50:42 字数 1609 浏览 3 评论 0原文

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

I have a blocking function that executes an asynchronous MySQL query and returns the result when it is obtained. The reason is is asynchronous is this program is not allowed to lock up during a query.

The function is called when the user presses a button, so the function may get called several times before the first query completes. I thought I could add a boolean to check whether or not a query is executing and have the function wait until it's done before continuing, but it is not working as intended. There is some issue with the two DoEvents() I use. If you comment out either one, it runs just fine, except the UI freezes.

How can I make the function do a non-blocking wait while a query is executing, as well as do a non-blocking wait while the query itself is being fetched? I would really prefer to keep this on one thread, as the function itself is blocking to the code that called it. Any help would b e greatly appreciated!

    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;
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

晨与橙与城 2024-08-19 11:50:42

您不应使用 DoEventsSleep 来创建响应式 UI。有关在 UI 中执行异步操作的信息,请参阅 BackgroundWorker 类。

You should not be using DoEvents and Sleep to create a responsive UI. For performing asynchronous operations in the UI, see the BackgroundWorker class.

生活了然无味 2024-08-19 11:50:42

执行异步工作的方法有很多种,从直接使用线程池到像BackgroundWorker 这样的助手。

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

现在来看看您的代码的实际问题。正如 Adam 所指出的,您确实不应该使用 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!");
        }
    }

There are numerous ways to do async work, from using the thread pool directly to helpers like BackgroundWorker.

However this does not answer your primary question which is a little contradictory, i.e. you want to do a non-blocking wait. I would suggest that you not block at all which is to say, if you are already executing then ignore the request and do nothing. You may want to give some feedback to say "already working" in this situation.

Now to actual issues with your code. As Adam has noted you really shouldn't be using DoEvents and Sleep. Instead you post the long running work item to some background task and use a flag to synchronise between the UI thread and the thread running your task, e.g.

    /// <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!");
        }
    }
似梦非梦 2024-08-19 11:50:42

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

请参阅:
异步入门,了解新功能的介绍特点
这里是一个专门引用 MySQL 连接的问题。

Though it wasn't an option when you asked your question, if you can upgrade to .NET 4.5 there is now a much cleaner way to to asynchronous operations while still essentially writing in the same fashion you would for synchronous code. This involves using the new async and await keywords.

See:
An Async Primer for an intro to the new features
And here is a SO question specifically referencing MySQL connections.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文