GTK# Приложение.Вызов не работает

#c# #.net #multithreading #mono #gtk

#c# #.net #многопоточность #mono #gtk

Вопрос:

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

Приложение.Похоже, что вызов не работает без вызова приложения.Инициализация, даже при запуске моего собственного GLib.MainLoop.

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

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

Ответ №1:

Если вы хотите асинхронную обработку, которая не обязательно должна выполняться в определенном потоке, взгляните на систему.Многопоточность.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. Отлично! В итоге я пока использовал QueueUserWorkItem. Спасибо! 🙂

Ответ №2:

Приложение.Вызов в основном работает путем сохранения списка делегатов для запуска.

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

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

 public static class Application {

   public static void Invoke ( EventHandler dothis ) {
      if ( dothis != null ){ 
         dothis( null, null ); }
   }
}
  

Комментарии:

1. Предполагается, что класс, вызывающий эти вызовы, запускает свои события асинхронно, поскольку он должен продолжать обрабатывать данные «на лету». Их синхронный вызов может привести к зависанию основной обработки, что приведет к потере данных или их искажению. По сути, этот поток имеет дело с оборудованием, над которым у нас нет контроля.

2. Я искал, какой класс потоков я должен использовать, чтобы имитировать какое приложение. Вызов выполняется за кулисами.

3. Приложение. Invoke всегда вызывает все, что находит в основном цикле GTK. Если у вас есть поток аппаратной обработки, то вам нужно выполнить команду «добавить в список».

4. Правильно. аппаратная обработка происходит в потоке, отличном от gui. Следовательно, мне нужно поместить его в очередь в какой-нибудь другой пул потоков. В итоге я использовал ThreadPool.QueueUserDelegate . Я жду, чтобы протестировать на безголовой машине.

Ответ №3:

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

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

Как оказалось, статический конструктор приложения вызывает GLib.Нитки.Инициализация (), которая по сути является бомбой замедленного действия. В документации GLib указано, что GLib.Нитки.Инициализация должна вызываться при использовании нескольких потоков, и это при GLib.Нитки.Инициализация когда-либо вызывается, она должна быть вызвана ПЕРЕД любым другим использованием GLib.

Итак, в коде, с которым я работал, мы добавили делегат в GLib.Timeout после применения.Инициализация, но перед применением.Запускайте и перед любыми вызовами приложения.Вызов. Это означает, что мы были в безопасности, потому что Application.Инициализация вызовет статический конструктор приложения, следовательно, вызовет GLib.Поток.Инициализация. Это было хорошо. Однако, когда мы удалили приложение.Инициализация и вызванный тайм-аут.Сначала добавьте поток.Инициализация еще не была вызвана. Это означало, что если бы мы вызвали Thread.При последующем запуске потоки, тайм-ауты, делегаты и т.д. прекратились бы.

Конечно же, Приложение.Вызывать или применять.Run вызвал бы статический конструктор Application, который, в свою очередь, вызвал бы GLib.Поток.Инициализация. Это вызвало проблему.

TLDR;

Короче говоря, убедитесь, что вы вызываете статический конструктор приложения, прежде чем использовать тайм-аут.Добавьте в свой код приложения. Не звони Бойкому.Нитки.Инициализируйте вручную, потому что его повторный вызов в 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();