WPF工作线程需要通过消费者通知暂停

时间:2010-05-20 12:04:41

标签: c# wpf worker worker-thread

嗨,我有一个生产者 - 消费者模式。 WPF UI对于长时间运行的工作线程是有用的,它正在侦听数据包和排队任务。实际上,所有消息都会出列,然后是UI消费者进程。

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

我通过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);
        }
    }

1 个答案:

答案 0 :(得分:0)

如果我理解你,你希望生产者线程在UI窗口数量达到某个阈值时等待。我将使用生产者和使用者共享的信号量,并初始化为最大窗口数以完成此行为。

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

在每次入队之前,请致电

semaphore.WaitOne();

完成每项任务并关闭其窗口后,请致电

semaphore.Release();

一旦MaxWindowCount项目入队,您的制作人将等待下一次 WaitOne()的调用,并等待 Release()被调用。

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

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

- 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(任务)的调用将调用BeginInvoke。否则,它调用WaitOne()并等待其中一个窗口发出信号表明它们已关闭。我们可以在这里使用信号量,在每次成功创建窗口后调用WaitOne()。每个窗口都负责在关闭时调用Release()。

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