2011-11-09 49 views
0

我正在通過使用Application.Invoke的許多庫來緊密綁定到GTK#的應用程序。不幸的是,我們將應用程序(服務器類型的應用程序)移植到沒有窗口管理器的系統中,所以當我們初始化GTK時,它現在崩潰了。GTK#Application.Invoke不工作

如果不調用Application.Init,即使運行我自己的GLib.MainLoop,Application.Invoke也不會工作。

我正在尋找合適的替代Application.Invoke。我應該如何去替換應用程序使用的庫中的Application.Invoke,以便我可以移除對GTK的依賴關係?

注意:我已經提出了一個重構,以從應用程序和域代碼中刪除GUI並將其移到視圖中,但現在已被擊落。我基本上試圖讓它在沒有窗口管理器的系統上運行。

回答

0

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用法之前調用。

因此,在我正在使用的代碼中,我們添加了一個委託給GLOB.Timeout Application.Init,但在Application.Run之前,以及對Application.Invoke的任何調用之前。這意味着我們是安全的,因爲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(); 
1

Application.Invoke的基本工作原理是保持代表運行的列表。

每次GTK主循環迭代它都檢查這個列表並執行它找到的任何東西。這聽起來像你需要一個像這樣循環的後臺線程。這就是說,我無法想象在非圖形應用程序中如何或爲什麼需要這種循環調用,您可能只需直接在那裏調用即可。例如:

public static class Application { 

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

類調​​用這些所調用是假設它是異步激發它的事件,因爲它必須保持處理數據。同步調用它們會掛起主要的處理過程,導致數據丟失或數據中斷。基本上,該線程正在處理我們無法控制的硬件。 –

+0

我在尋找什麼線程類來模擬/模擬Application.Invoke在幕後做些什麼。 –

+0

Application.Invoke總是調用它在主GTK循環中找到的任何東西。如果你有一個硬件處理線程,那麼你需要做'添加到列表'的東西。 – IanNorton

2

如果它是異步處理你想,這並不需要在一個特定的線程的情況發生,看看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(); 
     } 
    } 
} 
+0

優秀!我現在結束了使用QueueUserWorkItem。謝謝! :) –