嗨,我有一个生产者 - 消费者模式。 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);
}
}
答案 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的信息。