【问题标题】:GTK# Application.Invoke Not WorkingGTK# Application.Invoke 不工作
【发布时间】:2011-11-09 21:11:05
【问题描述】:

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

如果不调用 Application.Init,Application.Invoke 似乎无法工作,即使在运行我自己的 GLib.MainLoop 时也是如此。

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

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

【问题讨论】:

    标签: c# .net multithreading mono gtk


    【解决方案1】:

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

    【讨论】:

    • 太棒了!我现在最终使用了 QueueUserWorkItem。谢谢! :)
    【解决方案2】:

    Application.Invoke 基本上是通过保存要运行的委托列表来工作的。

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

    也就是说,我无法想象您如何或为什么需要在非图形应用程序上进行这种循环调用,您可能只需直接在那里调用就可以得到同样的服务。例如:

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

    【讨论】:

    • 调用这些调用的类假设异步触发它的事件,因为它必须保持动态处理数据。同步调用它们可能会挂起主处理,导致数据丢失或数据损坏。基本上,该线程正在处理我们无法控制的硬件。
    • 我正在寻找我应该使用什么线程类来模仿/模拟 Application.Invoke 在幕后所做的事情。
    • Application.Invoke 总是调用它在 GTK 主循环中找到的任何东西。如果您有硬件处理线程,那么您需要执行“添加到列表”的操作。
    • 对。硬件处理发生在非 gui 线程上。因此,我需要将它排入其他线程池。我最终使用了ThreadPool.QueueUserDelegate。我正在等待在无头机器上进行测试。
    【解决方案3】:

    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 之前调用它。

    因此,在我正在使用的代码中,我们在 Application.Init 之后,但在 Application.Run 之前以及对 Application.Invoke 的任何调用之前,向 GLib.Timeout 添加了一个委托。这意味着我们是安全的,因为 Application.Init 将调用 Application 的静态构造函数,因此调用 GLib.Thread.Init。这很好。但是,当我们删除 Application.Init 并首先调用 Timeout.Add 时,尚未调用 Thread.Init。这意味着如果我们稍后调用 Thread.Init,线程、超时、委托等会阻塞。

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

    TLDR;

    长话短说,确保在应用程序代码中使用 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();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多