2009-02-03 15 views
5

Sto cercando di creare un'applicazione C# multipiattaforma usando C#, mono/GTK # su Linux e .NET/GTK # su Windows, tuttavia la sequenza di avvio sembra essere leggermente diversa sotto le due piattaforme:Threading multipiattaforma e GTK #, non funziona (correttamente)?

sotto Linux:

public static void Main (string[] args) 
{ 
    Gdk.Threads.Init(); 
    // etc... 

In Windows:

public static void Main (string[] args) 
{ 
    Glib.Thread.Init(); 
    Gdk.Threads.Init(); 
    // etc... 

Entrambi richiedono che sia fatto in questo modo: Windows lamenta g_thread_init() non essere chiamato con il codice di Linux, e Linux lamenta già essere chiamato con il codice di Windows. Oltre a questo, funziona tutto meravigliosamente.

Il mio primo tentativo di una "soluzione" sembrava:

public static void Main (string[] args) 
{ 
    try { 
     Gdk.Threads.Init(); 
    } catch (Exception) { 
     GLib.Thread.Init(); 
     Gdk.Threads.Init(); 
    } 
    // etc... 

Ma anche questa soluzione disordinata non funziona; l'errore proviene da GTK +, codice non gestito, quindi non può essere catturato. Qualcuno ha idee brillanti sulla causa del problema e su come risolverlo? Oppure, in mancanza di ciò, hanno idee brillanti su come individuare quale debba essere chiamato in fase di runtime?

risposta

8

Gtk + su Win32 non supporta correttamente il threading. Devi fare tutte le tue chiamate GUI dallo stesso thread che hai chiamato Gtk.Main().

In realtà non è così male come sembra. C'è un trucco che puoi usare per inviare le funzioni al thread principale. Basta usare GLib.Idle.Add() da qualsiasi thread e la funzione verrà chiamata a breve nello stesso thread in cui è in esecuzione il loop principale. Ricorda semplicemente di restituire false nella funzione di gestione idle, o continuerà a funzionare per sempre.

Se si seguono e si utilizzano le tecniche di cui sopra, non è nemmeno necessario chiamare Gdk.Threads.Init().

+0

Johan ha ragione: non hai davvero bisogno di chiamare le funzioni GDK da più thread, ed è una cattiva idea farlo comunque (Winforms e WPF non ti permettono nemmeno di farlo affatto) –

+0

Grazie per aver fatto il miglio supplementare e cercare le chiamate GTK # :) –

+0

Woho! Grazie per i 300 punti! –

0

Umm, hai provato a decorare il tuo metodo principale con l'attributo [STAThread]?

ad es.

#if !Mono //or whatever 
[STAThread] 
#endif 
public static void Main (string[] args) 
{ 
    Gdk.Threads.Init(); 
    ... 
} 

In mancanza di questo si potrebbe usare la compilazione condizionale in questo modo ...

public static void Main (string[] args) 
{ 
    Gdk.Threads.Init(); 
#if !Mono //or whatever 
    Gdk.Threads.Init(); 
#endif 
    ... 
} 
+0

Ho l'impressione che STAThread abbia effetto solo su COM, quindi questo non significa nulla in questa istanza. –

2

io non sono sicuro se questo funziona su Mono, ma è possibile utilizzare la classe di ambiente per determinare quale sistema operativo il programma sta funzionando.

public static void Main (string[] args) 
{ 
    PlatformID platform = Environment.OSVersion.Platform; 
    if (platform == PlatformID.Win32NT || 
     platform == PlatformID.Win32S || 
     platform == PlatformID.Win32Windows) 
     Glib.Thread.Init(); 
    else if (platform != PlatformID.Unix) 
     throw new NotSupportedException("The program is not supported on this platform"); 

    Gdk.Threads.Init(); 
    // etc... 

Il PlatformID enum contiene più di un semplice Windows e Unix però, così si dovrebbe verificare la presenza di altri valori.

1

In realtà, questo potrebbe essere un bug nelle associazioni C# per GDK o nella versione di GDK. Secondo la documentazione per gdk\_threads\_init(), g\_thread\_init() deve essere chiamato prima, e la # documentazione di GTK dice la stessa:

GLib.Thread.Init() has to be called before Gdk.Threads.Init() .

Sulla mia macchina Linux (con GDK 2.14.4), un programma C che chiama gdk\_threads\_init() senza aver chiamato g\_thread\_init() stampa un messaggio di errore e termina con un errore. Ti sei assicurato di avere la stessa versione GDK su Linux e Windows e che la versione su Linux (se diversa dalla versione Windows) richiede anche la chiamata a g\_thread\_init(), o se è qualcosa che è stato cambiato tra i due versioni. Infine, verificare che questo programma termina con un errore:

#include <gdk/gdk.h> 

int main(int argc, char **argv) { 
    gdk_threads_init(); 

    return 0; 
}

compilarlo con gcc -o test `pkg-config --cflags --libs gdk-2.0` test.c
(supponendo che hai salvato come test.c.)

Se questo programma termina con un errore, è un bug nella libreria GDK #.
In caso contrario, si tratta di un bug nella versione GDK.

+0

Non è che non viene chiamato, viene chiamato, viene chiamato come parte del wrapper in linux e non viene chiamato come parte del wrapper in windows. Sto testando con le stesse versioni di GTK # su entrambi, ma non sono sicuro delle versioni di gtk +. –

+0

Confermato dal tuo piccolo test, la differenza è sicuramente in GTK #, ma sto usando la stessa versione tra Linux e Windows. –

+0

Ho provato a guardare i sorgenti GTK #, ma non riesco a trovare nessun posto ovvio che Gdk.Threads.Init() chiama g \ _thread \ _init(), quindi penso che la prossima tappa dovrebbe essere gli sviluppatori GTK # 'mailing list o forum, ma questo mi sembra strano. – arnsholt