0

嗨,我有一个生产者 - 消费者模式。WPF UI 是一个长期运行的工作线程的辅助工具,它正在侦听数据包和排队任务。在实践中,所有消息都出队,然后是 UI 消费者进程。

我遇到的问题是我有一个负责所有 WPF GUI 组件的 UIcontroller 类。它会打开一个新窗口,显示每个出队的任务,并保存一个线程安全的引用窗口集合。

我通过 Despatcher.BeginInvoke 发出信号。那里没有惊喜。

我遇到的问题是,如果 UIController 类成功打开 cconfigured 数量的窗口,我想暂停/从字面上停止我的工作线程。打开的窗口数量有限制。一旦打开的窗口数量减少到界限内,我就会恢复队列的处理。我尝试在此类中保存我的窗口集合,并通过调用将其传递给 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 窗口的数量处于某个阈值时等待。我将使用生产者和消费者共享的信号量并初始化为最大窗口数来完成此行为。

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

在每次入队之前,您调用

semaphore.WaitOne();

一旦每个任务完成并且它的窗口关闭,调用

semaphore.Release();

一旦 MaxWindowCount 项已入队,您的生产者将等待下一次调用WaitOne()并等待直到调用Release()

嗯,这可能无法完成您想要的,因为它将队列中的项目数限制为 MaxWindowCount ,而不一定是打开窗口的数量。它假定排队中的每个项目都有一个打开的窗口,在您的情况下可能不是这样。

如果这是一个无效的假设,您可以有一个线程负责从队列中拉取项目。该线程将负责在将项目移交给 UIController 时调用WaitOne() 。UIController 仍然负责调用Release()。实际上,您将有两个生产者-消费者队列。一个用于任务的无界队列和一个用于窗口的有界队列。

-- 2010 年 5 月 21 日添加 --

另一种选择可能是对 CreateWindow 的调用采用回调参数,告诉控制器打开窗口的数量。

使用以下委托作为 CreateWindow 的参数,您可以保留 Worker 中打开窗口的计数,并在达到最大值时采取相应措施。

public delegate void CreateWindowCallback( int numOpenWindows );

您可能会在您的工作人员中保留打开窗口的数量,并且您需要让工作人员知道最大窗口数量。或者,您可以在 Controller 类中保留一个 CanOpenNewWindows 布尔值。

在调用_UIthread.BeginInvoke之前,worker 可以检查当前窗口计数(或 CanOpenNewWindows 属性),如果它不应该打开一个新窗口,则调用 WaitOne()。您仍然需要知道窗口何时关闭,以便释放/通知工作人员继续。

如果窗口创建失败,或者控制器不需要显示窗口,您可以让工作人员知道打开的窗口计数没有增加。这确实假设 _controller 知道是否创建了窗口。

——另一个想法——

可以将所有窗口创建逻辑保留在 UIController 中。UIController 需要知道 UIThread 并修改 CreateWindow 调用以只接受一个任务。

如果打开的窗口少于最大窗口,则对CreateWindow(task)的调用将调用 BeginInvoke。否则,它会调用 WaitOne() 并等待其中一个窗口发出已关闭的信号。我们可以在这里使用信号量,在每次成功创建窗口后调用 WaitOne()。每个窗口都将负责在关闭时调用 Release()。

这样,当工作人员直接调用 CreateWindow 时,它会在达到最大窗口计数时阻塞,并且它永远不必知道最大窗口计数或有关 UI 的其他信息。

于 2010-05-20T15:00:46.443 回答