Синхронный / блокирующий Application.Invoke () для GTK # - PullRequest
3 голосов
/ 06 февраля 2010

К сожалению, 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 ]

2 голосов
/ 06 февраля 2010

Что ж, да, нет причин ждать, пока делегат выполнится, чтобы получить правильное возвращаемое значение. Исправлено:

int result = i + 1;

И можно разрешить асинхронное выполнение OutputStringToUserInterface () при условии, что вы не вызываете ThreadFunction () так часто, что он заполняет поток пользовательского интерфейса запросами.

Если ваш реальный код на самом деле зависит от возвращаемого значения из функции, которая должна выполняться в потоке пользовательского интерфейса, то нет, вы не можете сделать это быстрее. Очевидно, это то, чего вы действительно хотите избежать.

1 голос
/ 24 декабря 2014

Вы можете инкапсулировать свой текущий код в универсальную оболочку:

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();});
}
...