首页 文章

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

提问于
浏览
0

嗨,我有一个 生产环境 者 - 消费者模式 . 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

    如果我理解你,你希望 生产环境 者线程等待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(task) 的调用将调用BeginInvoke . 否则,它调用WaitOne()并等待其中一个窗口发出信号表明它们已关闭 . 我们可以在这里使用信号量,在每次成功创建窗口后调用WaitOne() . 每个窗口都负责在关闭时调用Release() .

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

相关问题