超时调用

问题描述 投票:1回答:2

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

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”

即使前两件事情可以解决,我们仍然有一般的ickyness。有没有更好的方法来解决这个问题?

.net timeout invoke begininvoke
2个回答
0
投票

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


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线程,使用它时应该没有更多或道德的“代码味道”问题。我们应该使用任何工具或技术来帮助我们完成工作。

© www.soinside.com 2019 - 2024. All rights reserved.