【问题标题】:Invoke method of the control in context of its thread from separate static class从单独的静态类调用控件在其线程上下文中的方法
【发布时间】:2014-10-02 16:58:49
【问题描述】:

我有一个表单和一些控件:

public class Tester : Form
{
    public Label Demo;

    public Label GetDemo()
    {
        return Demo.Text;
    }
}

我还有一些静态类:

public static bool Delay(Func<bool> condition)
{
    bool result = false;
    AutoResetEvent e = new AutoResetEvent(false);

    Timer t = new Timer(delegate {
        if (result = condition()) e.Set(); // wait until control property has needed value
    }, e, 0, 1000);

    e.WaitOne();
    t.Dispose();

    return result;
}

有时控制会创建新线程并调用我们的静态方法:

ThreadPool.QueueUserWorkItem(delegate {
    if (Delay(() => GetDemo() == "X")) MessageBox.Show("X");
}, null);

当然,这会导致异常,因为 GetDemo 将被传递给 Delay,并将作为委托在新线程中调用。

当然可以通过Invoke调用我们的静态方法来解决:

ThreadPool.QueueUserWorkItem(delegate {
    Invoke((MethodInvoker) delegate {
        if (Delay(() => GetDemo() == "X")) MessageBox.Show("OK");
    }
}, null);

很遗憾,我不能更改延迟的调用,我只能更改它的实现。

问题:

1) INSIDE 静态方法 Delay 需要更改哪些内容,以便 condition() 在其本机线程中执行 GetDemo 而无异常?

2) 是否可以在延迟中执行类似的操作?

SynchronizationContext.Dispatcher((Action) delegate {  
    if (condition()) e.Set();
});

【问题讨论】:

  • 与其不断检查是否满足条件,无论代码会导致满足该条件,都应该触发一个事件,然后该代码可以添加一个事件处理程序。这将使代码更简单,并且会使程序异步,因此不会阻塞 UI 线程。
  • 结果总是假的。你的意思是把它分配到某个地方吗?
  • @Dave Mackersie :抱歉,已更正,是的,它有一个任务
  • @Servy :您能否澄清一下代码的哪一部分应该触发事件?据我所知,condition() 是一个委托,它调用原始方法 GetDemo(),它看起来像一种事件模型,但这不起作用,因为任何事件都会从错误的线程调用 GetDemo()。我错过了什么吗?
  • 显然调用代码在这里有问题。它不仅仅停留在您的 Delay() 方法上,在线程池线程上显示消息框也是荒谬的。在 Delay() 中解决这个问题只会造成哥特式的混乱。这不是你的错误,请继续。

标签: c# multithreading thread-safety invoke synchronous


【解决方案1】:

此解决方案假定您的代码中的其他位置可以接收 UI 线程上的较早调用,以保存 UI SynchronizationContext 的副本。情况可能并非如此,在这种情况下,我提出的解决方案将不起作用。

// Assign this using SynchronizationContext.Current from a call made on the UI thread.
private static SynchronizationContext uiSynchronizationContext;

public static bool Delay(Func<bool> condition)
{
    bool result = false;
    AutoResetEvent e = new AutoResetEvent(false);

    Timer t = new Timer(delegate 
    {
        uiSynchronizationContext.Send(s => result = condition(), null);

        if (result)
            e.Set(); // wait until control property has needed value
    }, e, 0, 1000);

    e.WaitOne();
    t.Dispose();

    return result;
}

【讨论】:

  • 恭喜,您的应用程序刚刚死锁。
  • 我不这么认为。但是再看一遍,我使用了错误的同步上下文。延迟是从工作线程调用的,而不是从 UI 线程调用的。我需要以某种方式获取 UI 同步上下文。
  • 如果您成功编组到 UI 线程,您就会使程序陷入僵局。当然,如果你失败了,那么你没有回答这个问题。
  • 好的,我已经编辑了我的结果以使用 UI 同步上下文。现在,您在哪里看到了僵局?计时器线程编组对 UI 线程的调用,该调用应立即返回。你看到 UI 线程在某处被阻塞了吗?
  • 在原始版本中阻塞了 UI 线程。编辑后,您根本无法解决问题,因为此方法不会在不重构方法调用方式的情况下编组到 UI 线程。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-27
  • 2023-02-01
  • 1970-01-01
相关资源
最近更新 更多