0

非同期の MySQL クエリを実行し、取得時に結果を返すブロッキング関数があります。理由は非同期であり、このプログラムはクエリ中にロックすることが許可されていません。

ユーザーがボタンを押すと関数が呼び出されるため、最初のクエリが完了するまでに関数が数回呼び出される場合があります。ブール値を追加して、クエリが実行されているかどうかを確認し、関数が完了するまで待機してから続行できると考えましたが、意図したとおりに機能していません。私が使用する 2 つの DoEvents() には問題があります。いずれかをコメントアウトすると、UI がフリーズすることを除いて、問題なく動作します。

クエリの実行中に関数をノンブロッキング待機させ、クエリ自体のフェッチ中にノンブロッキング待機をさせるにはどうすればよいですか? 関数自体がそれを呼び出したコードをブロックしているため、これを1つのスレッドに保持することを本当に好みます。どんな助けでも大歓迎です!

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

3 に答える 3

6

DoEventsとを使用Sleepしてレスポンシブ UI を作成しないでください。UI で非同期操作を実行するには、BackgroundWorkerクラスを参照してください。

于 2009-11-25T03:15:00.890 に答える
3

スレッド プールを直接使用する方法から、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!");
        }
    }
于 2009-11-25T05:56:08.973 に答える
1

質問したときはオプションではありませんでしたが、.NET 4.5にアップグレードできる場合は、同期コードの場合と基本的に同じ方法で記述しながら、非同期操作を行うためのはるかにクリーンな方法があります。これには、newキーワードasyncawaitキーワードの使用が含まれます。

新機能の概要については、非同期入門書を参照し て
ください。MySQL接続を具体的に参照するSOの質問があります。

于 2013-02-08T20:31:00.913 に答える