同步/阻止Application.Invoke()for GTK#

时间:2010-02-06 13:31:34

标签: c# .net mono multithreading gtk#

不幸的是,Application.Invoke()是异步的:

private string ThreadFunction(int i)
{
    string result = null;

    Gtk.Application.Invoke(delegate
    {
        OutputStringToUserInterface("i = " + i.ToString());
        result = GetStringFromUserInterface();
    });

    return result;
}

这意味着在此示例中,ThreadFunction()在调用Application.Invoke()后立即进行,导致result字符串可能未定义的状态。 - 通常ThreadFunction()会更快,并会返回旧值(即null)。


这是一种使用ManualResetEvent使Application.Invoke()同步的解决方法:

private string ThreadFunction(int i)
{
    string result = null;

    using (var ev = new ManualResetEvent(false))
    {
        Gtk.Application.Invoke(delegate
        {
            OutputStringToUserInterface("i = " + i.ToString());
            result = GetStringFromUserInterface();

            ev.Set();
        });

        ev.WaitOne();
    }

    return result;
}

这样,ThreadFunction()会等到Application.Invoke()返回,就像使用WinForms Control.Invoke()一样。

编辑:更好的示例代码

EDIT2 :添加缺失的using


现在我的问题是:有更好的解决方案吗?

2 个答案:

答案 0 :(得分:2)

嗯,是的,没有理由等待委托执行以获得正确的返回值。修正:

int result = i + 1;

可以让OutputStringToUserInterface()以异步方式执行,假设您不经常调用ThreadFunction(),因为它会使请求充满UI线程。

如果你的真实代码实际上依赖于必须在UI线程上运行的函数的返回值,那么,不,你不能让它更快。显然,这是你想要避免的事情。

答案 1 :(得分:1)

您可以将当前代码封装到通用包装器中:

public static void GuiInvoke(Action action)
{
    var waitHandle = new ManualResetEventSlim();
    Gtk.Application.Invoke( (s,a) => 
                            {
                                action();
                                waitHandle.Set();
                            });
    waitHandle.Wait();
}

public static void BeginGuiInvoke(Action action)
{
    Gtk.Application.Invoke( (s,a) => {action();});
}