GTK # Application.Invoke не работает - PullRequest
0 голосов
/ 10 ноября 2011

Я работаю над приложением, которое тесно связано с GTK # с помощью Application.Invoke через многие его библиотеки. К сожалению, мы портируем приложение (приложение серверного типа) в систему без оконного менеджера, поэтому в настоящий момент он падает, когда мы инициализируем GTK.

Application.Invoke, похоже, не работает без вызова Application.Init, даже когда я запускаю свой собственный GLib.MainLoop.

Я ищу подходящую замену для Application.Invoke. Как мне пойти о замене Application.Invoke в библиотеках, используемых приложением, чтобы я мог удалить зависимость от GTK?

Примечание. Я предложил рефакторинг, чтобы избавиться от графического интерфейса приложения и кода домена и переместить его в представление, но на данный момент это сбито. Я в основном пытаюсь заставить его работать в системе без оконного менеджера.

Ответы [ 3 ]

2 голосов
/ 10 ноября 2011

Если вам нужна асинхронная обработка, которая не должна выполняться в конкретном потоке, взгляните на System.Threading.ThreadPool.QueueUserWorkItem . Основная проблема в этом подходе заключается в том, что вы сами должны обеспечить безопасность потоков.

Если вам нужно, чтобы это произошло в главном потоке, вам нужно будет создать список делегатов для периодического вызова и опроса этого списка в главном потоке (или ждать, пока что-то будет опубликовано в нем):

using System.Collections.Generic;
using System.Threading;
class Main {
    Queue<Action> actions = new Queue<Action> ();
    ManualResetEvent the_event = new ManualResetEvent (false);
    public void Invoke (Action action)
    {
        lock (actions) {
            actions.Enqueue (action);
            the_event.Set ();
        }
    }
    public void Poll ()
    {
        Action action = null;
        lock (actions) {
            if (actions.Count > 0) {
                action = actions.Dequeue ();
            }
        }
        if (action != null)
            action ();
    }
    public void Wait ()
    {
        Action action = null;
        while (true) {
            the_event.WaitOne ();
            lock (actions) {
                if (actions.Count > 0) {
                    action = actions.Dequeue ();
                } else {
                    the_event.Reset ();
                }
            }
            if (action != null)
                action ();
        }
    }
}
1 голос
/ 10 ноября 2011

Application.Invoke в основном работает, сохраняя список выполняемых делегатов.

Каждый раз, когда основной цикл GTK повторяется, он проверяет этот список и выполняет все, что находит.Похоже, вам нужен фоновый поток, который выглядит как этот.

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

public static class Application {

   public static void Invoke ( EventHandler dothis ) {
      if ( dothis != null ){ 
         dothis( null, null ); }
   }
}
0 голосов
/ 15 декабря 2011

Application.Invoke не требует замены , а не (по крайней мере, для той версии, которую я использую).Это было ошибочное мнение.Application.Inoke просто поворачивается и добавляет делегата в GLib.Timeout с таймаутом, установленным в 0, и возвращает «false», поэтому срабатывает только один раз.

Вместо того, чтобы избавиться от Application.Invoke, я попыталсячтобы выяснить, почему мои делегаты не стреляли при использовании Application.Invoke без Appliation.Run или Application.Init.Имейте в виду, что я уже запустил свой собственный GLib.MainLoop.

Как оказалось, статический конструктор приложения вызывает GLib.Thread.Init (), который по сути является бомбой замедленного действия.Документация GLib гласит, что GLib.Thread.Init должен вызываться при использовании нескольких потоков, и что если GLib.Thread.Init когда-либо , то он должен вызываться ДО любого другого использования GLib.

Итак, в коде, с которым я работал, мы добавили делегата в GLib.Timeout после Application.Init, но перед Application.Run и перед любыми вызовами Application.Invoke.Это означает, что мы были в безопасности, потому что Application.Init вызывал бы статический конструктор Application, поэтому вызывал GLib.Thread.Init.Это было хорошоОднако когда мы удалили Application.Init и сначала вызвали Timeout.Add, Thread.Init еще не был вызван.Это означало, что если бы мы позже вызвали Thread.Init, то потоки, тайм-ауты, делегаты и т. Д. Задохнулись бы.

Конечно, Application.Invoke или Application.Run вызывали бы статический конструктор Application, который, в свою очередь,будет вызывать GLib.Thread.Init.Это вызвало проблему.

TLDR;

Короче говоря, убедитесь, что вы вызываете статический конструктор Application перед использованием Timeout.Add в своем коде приложения.Не вызывайте Glib.Thread.Init вручную, потому что повторный вызов в Mono вызовет сбой приложения.

Это нормально:

Application.Init();
Timeout.Add(0, delegate { return false; });
Application.Invoke(delegate { Console.WriteLine("Hey"); });
Application.Run();

Это разрушит вашу жизнь:

// Application.Init();
Timeout.Add(1000, delegate { return false; });
Application.Invoke(delegate { Console.WriteLine("Hey"); });
new MainLoop().Run();
//Application.Run();

Но это нормально:

// Application.Init();
Application.Invoke(delegate {});
Timeout.Add(1000, delegate { return false; });
Application.Invoke(delegate { Console.WriteLine("Hey"); });
new MainLoop().Run();
//Application.Run();
...