gtk#应用程序.调用不工作

本文关键字:工作 调用 应用程序 gtk# | 更新日期: 2023-09-27 18:14:15

我正在开发一个使用application与gtk#紧密绑定的应用程序。调用通过它的许多库。不幸的是,我们正在将应用程序(服务器类型的应用程序)移植到没有窗口管理器的系统上,因此当我们初始化GTK时,它目前崩溃了。

应用程序。如果不调用Application, Invoke似乎无法工作。Init,即使运行我自己的GLib.MainLoop。

我正在寻找一个合适的替代Application.Invoke。我应该如何去更换应用程序。在应用程序使用的库中调用,以便我可以删除对GTK的依赖?

注意:我已经提出了一个重构,从应用程序和域代码中摆脱GUI,并将其移动到视图中,但现在已经被否决了。我基本上是想让它在没有窗口管理器的系统上运行。

gtk#应用程序.调用不工作

如果你想要的是不需要发生在特定线程上的异步处理,请查看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 ();
        }
    }
}

应用。Invoke基本上是通过保持一个委托列表来运行的。

每次GTK主循环迭代时,它都会检查这个列表并执行它找到的任何内容。听起来你需要一个像这样循环的后台线程。

也就是说,我无法想象你如何或为什么需要在非图形应用程序上进行这种循环调用,你可能会很好地直接调用。如:

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

应用。Invoke不需要替换(至少对于我正在使用的版本)。这是一种误解。应用程序。Inoke只是转身向GLib添加了一个委托。超时,超时设置为0,并返回"false",因此只触发一次。

而不是摆脱应用程序。调用时,我试图找出为什么我的委托在使用Application时没有触发。调用没有应用程序。运行或Application.Init。请记住,我已经启动了自己的GLib.MainLoop。

结果是,应用程序的静态构造函数调用GLib.Thread.Init(),这基本上是一个定时炸弹。GLib的文档说明GLib. thread . init必须在使用多个线程时调用,如果GLib. thread . init 调用,它必须在任何其他GLib使用之前调用。

所以,在我使用的代码中,我们向GLib添加了一个委托。应用程序后超时。Init,但在Application之前。运行,并在调用Application.Invoke之前运行。这意味着我们安全了,因为应用程序。Init将调用Application的静态构造函数,因此调用GLib.Thread.Init。这很好。然而,当我们删除Application。Init,并调用Timeout。首先添加线程。Init还没有被调用。这意味着如果我们调用Thread。Init之后,线程、超时、委托等将会阻塞。

果然,应用程序。调用或应用程序。Run会调用Application的静态构造函数,而Application又会调用GLib.Thread.Init。这导致了问题。

TLDR;

长话短说,确保在使用Timeout之前调用了Application的静态构造函数。添加您的应用程序代码。不要手动调用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();