0

こんにちは、プロデューサーとコンシューマーのパターンがあります。WPF UI は、パケットをリッスンしてタスクをキューに入れている、長時間実行されるワーカー スレッドの補助的なものです。実際には、すべてのメッセージがキューから取り出されてから、UI コンシューマーが処理します。

私が抱えている問題は、すべての WPF GUI コンポーネントを担当する UIcontroller クラスがあることです。デキューされた各タスクを表示する新しいウィンドウを開き、参照されたウィンドウのスレッドセーフなコレクションを保持します。

Despatcher.BeginInvoke で通知します。そこに驚きはありません。

私が抱えている問題は、UIController クラスが構成された数のウィンドウを正常に開いた場合に、ワーカー スレッドを一時停止/文字通り停止したいということです。開いているウィンドウの数には制限があります。開いているウィンドウの数が範囲内に戻ったら、キューの処理を再開します。このクラスでウィンドウ コレクションを保持し、参照によって更新される呼び出しを介して UI に渡そうとしましたが、Begin Invoke は非同期であり、ワーカー スレッドのループでカウントが時間内に更新されません。

UIcontrollerクラスで開いているウィンドウの数をテストし、そこからタスクを無視して再キューイングすることもできました-基本的に何もしません。しかし、私はよりクリーンなソリューションが欲しいです。

ある種のクリーンなコールバックを行うことはできますか?

    public void Work()
    {

        while (true)
        {

            Log.Instance.Info("****In worker THREAD " + _worker.Name);

            while (_controller.IncomingMessages.Count > 0 && [--test for no of windows open on GU thread somehow - I did try holding a reference to the collection in this class--])
            {
                try
                {
                    Log.Instance.Info("****In Notification worker THREAD and messages to process " + _worker.Name);

                    Messages.AlertMessage task = null;
                    lock (_locker)
                    {

                        if (_controller.IncomingMessages.Count > 0 && ToasterPopUps.Count < 5)
                        {
                            task = _controller.IncomingMessages.Dequeue();
                            if (task == null)
                            {
                                return;
                            }
                        }

                        if (task != null)
                        {
                            Log.Instance.Info("Dequeing: " + task + " " + task.ID + " from Notification thread");

                _UIthread.BeginInvoke(DispatcherPriority.Background, new JoinUIThread(DespatchUIThread), task, _UIthread);
                        }
                    }

                }
                catch (Exception err)
                {
                    Log.Instance.Critical(string.Format("Unexpected Error in PollPopUp Thread Queue {0} ", err));
                }

            }

            Log.Instance.Info("No more Notification tasks - wait for a signal");

            _wh.WaitOne();

        }

    }

    public void DespatchUIThread(Messages.AlertMessage task, System.Windows.Threading.Dispatcher dispatcherThread)
    {
        try
        {
            _controller.CreateWindow(task, dispatcherThread);
        }
        catch (Exception err)
        {
            Log.Instance.Critical("Critical error " + err);
        }
    }
4

1 に答える 1

0

UI ウィンドウの数が特定のしきい値に達している間、Producer スレッドを待機させたいとおっしゃっています。プロデューサーとコンシューマーによって共有され、最大ウィンドウ数に初期化されたセマフォを使用して、この動作を実現します。

const int MaxWindowCount = 5;
Sempahore semaphore = new Semaphore(0, MaxWindowCount );

各エンキューの前に、呼び出します

semaphore.WaitOne();

そして、各タスクが完了し、そのウィンドウが閉じられたら、呼び出します

semaphore.Release();

MaxWindowCount アイテムがキューに入れられると、プロデューサーはWaitOne()への次の呼び出しを待機し、 Release()が呼び出されるまで待機します。

うーん、これは、必ずしも開いているウィンドウの数ではなく、キュー内のアイテムの数を MaxWindowCount に制限しているため、目的を達成できない可能性があります。エンキューされた各アイテムには開いているウィンドウがあると想定していますが、これはあなたの場合には当てはまらない場合があります。

それが無効な仮定である場合は、キューからアイテムをプルする責任があるスレッドを持つことができます。このスレッドは、アイテムを UIController に渡すときにWaitOne()を呼び出す役割を果たします。UIController は引き続きRelease()の呼び出しを担当します。実際には、2 つのプロデューサー/コンシューマー キューを持つことになります。タスク用の 1 つの無制限キューと、ウィンドウ用の 1 つの制限付きキュー。

-- 2010年5月21日追記 --

もう 1 つのオプションは、CreateWindow の呼び出しにコールバック パラメーターを持たせ、開いているウィンドウの数をコントローラーに伝えることです。

次のデリゲートを CreateWindow のパラメーターとして使用すると、ワーカーで開いているウィンドウの数を保持し、最大数に達したときにそれに応じて動作させることができます。

public delegate void CreateWindowCallback( int numOpenWindows );

ワーカーで開いているウィンドウの数を保持することができ、ワーカーに最大ウィンドウ数を知らせる必要があります。または、Controller クラスに CanOpenNewWindows ブール値を保持することもできます。

_UIthread.BeginInvokeを呼び出す前に、ワーカーは現在のウィンドウ カウント (または CanOpenNewWindows プロパティ) を確認し、新しいウィンドウを開く必要がない場合は、WaitOne() を呼び出すことができます。ワーカーをリリース/シグナルして続行するために、ウィンドウが閉じられたときに何かを知る必要があります。

ウィンドウの作成に失敗した場合、またはコントローラーがウィンドウを表示する必要がない場合は、開いているウィンドウの数が増えていないことをワーカーに知らせることができます。これは、ウィンドウが作成されたかどうかを _controller が認識していることを前提としています。

-- もう一つの考え --

すべてのウィンドウ作成ロジックを UIController に保持できる場合があります。UIController は UIThread を認識し、タスクのみを受け入れるように CreateWindow 呼び出しを変更する必要があります。

開いているウィンドウが最大数よりも少ない場合、CreateWindow(task) を呼び出すと、BeginInvoke が呼び出されますそれ以外の場合は、WaitOne() を呼び出し、ウィンドウの 1 つが閉じたことを通知するまで待機します。ここでセマフォを使用して、ウィンドウの作成が成功するたびに WaitOne() を呼び出すことができます。各ウィンドウは、閉じるときに Release() を呼び出す責任があります。

このように、ワーカーが CreateWindow を直接呼び出すと、最大ウィンドウ カウントに達するとブロックされ、最大ウィンドウ カウントや UI に関する他の多くの情報を知る必要がなくなります。

于 2010-05-20T15:00:46.443 に答える