1

我们有一些代码在后台线程中运行,需要弹出对话框或其他一些用户交互,所以我们Invoke对 UI 线程进行通常的调用:

Control.Invoke(SomeFunction);

void SomeFunction()
{
  ...
}

但是,我们遇到了一个错误,我们的 UI 线程有时没有立即响应Invoke调用 - 我们跟踪到 UI 线程当前正在执行尚未返回的跨进程 DCOM 调用这一事实。一旦 DCOM 调用返回,我们的函数就会被调用,但在那之前,Invoke调用似乎已经挂起。

我对此的解决方案是引入超时:

ManualResetEvent invokeEvent = new ManualResetEvent();
var result = Control.BeginInvoke(SomeFunction, invokeEvent);

if (!invokeEvent.WaitOne(1000))
  throw new Exception("Not responding");

Control.EndInvoke(result);

void SomeFunction(ManualResetEvent invokeEvent)
{
  invokeEvent.Set();

  ...
}

这在“我的机器意义上的作品”中有效,但它有许多缺陷。


(来源:codinghorror.com

  • 首先,即使发生超时,该函数仍然会被调用——如果 DCOM 调用实际上没有完全挂起,它最终会运行
  • 其次,有明显可怕的比赛条件
  • 最后,整个事情的整体“Arrgh”-ness

即使前两件事可以解决,我们仍然有普遍的恶心。有没有更好的方法来解决这个问题?

4

2 回答 2

0

当涉及到在 GUI 线程上运行某些东西时,这是一个常见的线程问题,并且这种症状会影响所有类型的开发人员。

如果您要创建一个单独的线程来显示实际进度对话框和另一个线程来执行 DCOM 调用,则只需在两个线程之间移动 ManuaResetEvent 同步。这样做的好处是不会锁定 GUI 线程,因为创建进度表单的单独线程将创建自己的消息队列,并且用于运行 DCOM 调用的第二个线程不必锁定任何 GUI 线程。

它确实需要一些仔细的同步,但一旦完成,就可以看到它的实际效果:

private ManualResetEvent _event = new ManualResetEvent(false);
...

private void StartTheComProgressCall()
{
    _event.Reset();

    ThreadPool.QueueUserWorkItem(StartProgressDialog);
    ThreadPool.QueueUserWorkItem(StartDCOMCall);

    // there's various possibilities to perform here, we could ideally 1) wait on the
    // event to complete, 2) run a callback delegate once everything is done
    // 3) fire an event once completed
}

private void StartProgressDialog(object state)
{
    ProgressDialog dialog = new ProgressDialog();
    dialog.Show();

    while(!_event.WaitOne(0))
        Application.DoEvents();

    dialog.Close();
}

private void StartDCOMCall()
{
    ...
   <perform your DCOM routines here>

    // once the call is done, remember to trigger that it's complete
    // so that blocking threads can continue to do what they need to do
    _event.Set();
}

注释 有些人可能会反对使用该Application.DoEvents()方法,但考虑到这会DoEvents强制处理当前调用线程的消息队列上的任何待处理 Windows 消息,因为调用是在不同的线程(创建进度对话框的线程)而不是 GUI 中进行的线程,使用它应该没有更多或道德的“代码气味”问题。我们应该使用任何工具或技术来帮助我们完成工作。

于 2009-07-20T09:58:44.250 回答
0

将跨进程 DCOM 调用移动到另一个线程。您显然是在挂 UI 线程,这是完全不能接受的。解决这个问题,你的幻象问题(OP)也会消失。

于 2009-07-20T08:24:38.717 回答