#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();