简体   繁体   English

GTK#Application.Invoke无法正常工作

[英]GTK# Application.Invoke Not Working

I'm working on an application that has been tightly bound to GTK# by using Application.Invoke's through many of it's libraries. 我正在使用通过Application.Invoke的许多库紧密绑定到GTK#的应用程序。 Unfortunately, we're porting the application (server type application) to a system with no window manager, so it currently crashed when we initialize GTK. 不幸的是,我们将应用程序(服务器类型的应用程序)移植到没有窗口管理器的系统上,因此,当我们初始化GTK时,它当前崩溃了。

Application.Invoke doesn't seem to work without calling Application.Init, even when running my own GLib.MainLoop. 即使不运行Application.Init,Application.Invoke似乎也不起作用,即使在运行我自己的GLib.MainLoop时也是如此。

I am looking for a suitable replacement for Application.Invoke. 我正在寻找Application.Invoke的合适替代品。 How should I go about replacing Application.Invoke within the Libraries used by the application, so that I can remove the dependency on GTK ? 我应该如何替换应用程序使用的库中的Application.Invoke,以便删除对GTK的依赖?

Note: I've proposed a refactoring to get rid of the GUI from the app and domain code and move it into a view, but that's been shot down for now. 注意:我已经提出了一种重构,以摆脱应用程序和域代码中的GUI并将其移至视图中,但目前已被拒绝。 I'm basically trying to get it to run on a system with no window manager. 我基本上是想让它在没有窗口管理器的系统上运行。

If it's async processing you want that doesn't need to happen on a specific thread, have a look at System.Threading.ThreadPool.QueueUserWorkItem . 如果您希望异步处理不需要在特定线程上进行,请查看System.Threading.ThreadPool.QueueUserWorkItem The main problem about this approach is that you have to ensure thread safety yourself. 这种方法的主要问题是必须自己确保线程安全。

If you do need it to happen on the main thread, you'll need to create a list of delegates to invoke and poll that list periodically on the main thread (or wait for something to be posted to it): 如果确实需要在主线程上执行此操作,则需要创建一个委托列表,以在主线程上定期调用和轮询该列表(或等待将其张贴到主线程上):

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

Application.Invoke basically works by keeping a list of delegates to run. Application.Invoke基本上通过保留要运行的代表列表来工作。

Each time the GTK main loop iterates it checks this list and executes anything it finds. 每次GTK主循环迭代时,它都会检查此列表并执行找到的任何内容。 It sounds like you need a background thread that loops like this. 听起来您需要像这样循环的后台线程。

That said, I can't imagine how or why you need this looped invoke on a non-graphical app, you would probably be just as well served by simply calling directly there and then. 就是说,我无法想象您是如何或为什么需要在非图形应用程序上进行此循环调用的,只需直接在其中调用然后再为您提供服务就可以了。 Eg: 例如:

public static class Application {

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

Application.Invoke does not need to be replaced (at least for the version I am using). Application.Invoke 并不需要更换(至少我使用的版本)。 This was a misconception. 这是一个误解。 Application.Inoke simply turns around and adds a delegate to GLib.Timeout, with the timeout set to 0, and returns "false", therefore only firing once. Application.Inoke简单地转过身,并向GLib.Timeout添加一个委托,并将超时设置为0,并返回“ false”,因此仅触发一次。

Instead of getting rid of Application.Invoke, I attempted to find out why my delegates were not firing when using Application.Invoke without Appliation.Run or Application.Init. 没有尝试摆脱Application.Invoke,而是尝试找出为什么在没有Appliation.Run或Application.Init的情况下使用Application.Invoke时为什么我的代表没有被解雇。 Keep in mind that I had already started my own GLib.MainLoop. 请记住,我已经启动了自己的GLib.MainLoop。

As it turns out, the Application's static constructor calls GLib.Thread.Init(), which is basically a timebomb. 事实证明,应用程序的静态构造函数调用GLib.Thread.Init(),这基本上是一个定时炸弹。 GLib's documentation states that GLib.Thread.Init must be called when using multiple threads, and that if GLib.Thread.Init is ever called, it must be called BEFORE any other GLib usage. GLib的文档指出,使用多个线程时必须调用GLib.Thread.Init,并且如果曾经调用GLib.Thread.Init,则必须在使用任何其他GLib之前将其调用。

So, in the code I was working with, we added a delegate to GLib.Timeout after Application.Init, but before Application.Run, and before any calls to Application.Invoke. 因此,在我正在使用的代码中,我们 Application.Init之后但在Application.Run之前以及对Application.Invoke的任何调用之前,向GLib.Timeout添加了一个委托。 This means we were safe, because Application.Init would invoke the static constructor of Application, therefore invoking GLib.Thread.Init. 这意味着我们很安全,因为Application.Init会调用Application的静态构造函数,因此会调用GLib.Thread.Init。 This was good. 很好 However, when we removed Application.Init, and called Timeout.Add first, Thread.Init had not yet been called. 但是,当我们删除Application.Init并首先调用Timeout.Add时,尚未调用Thread.Init。 This meant that if we called Thread.Init later, the threading, timeouts, delegates, etc, would choke. 这意味着,如果我们稍后调用Thread.Init,线程,超时,委托等将被阻塞。

Sure enough, Application.Invoke or Application.Run would invoke the static constructor of Application, which in turn would invoke GLib.Thread.Init. 果然,Application.Invoke或Application.Run将调用Application的静态构造函数,而后者又将调用GLib.Thread.Init。 This caused the problem. 这引起了问题。

TLDR; TLDR;

Long story short, make sure you invoke Application's static constructor before using Timeout.Add in your application code. 长话短说,请确保在应用程序代码中使用Timeout.Add之前,先调用Application的静态构造函数。 Don't call Glib.Thread.Init manually, because calling it twice on Mono will make the application crash. 不要手动调用Glib.Thread.Init,因为在Mono上调用两次会导致应用程序崩溃。

This is okay: 没关系:

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

This will ruin your life: 这会毁了你的生活:

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

But this is fine: 但这很好:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM