Кроссплатформенность потоков и GTK #, не работает (правильно)? - PullRequest
5 голосов
/ 03 февраля 2009

Я пытаюсь создать кроссплатформенное приложение на C #, используя C #, mono / GTK # в Linux и .NET / GTK # в Windows, однако последовательность запуска должна немного отличаться для двух платформ:

Под Linux:

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

Под Windows:

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

Оба требуют, чтобы это делалось таким образом: Windows жалуется на то, что g_thread_init () не вызывается с кодом Linux, а Linux жалуется на то, что он уже вызывается с кодом Windows. Кроме этого, все это прекрасно работает.

Моя первая попытка «решения» выглядела так:

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

Но даже это грязное решение не работает; ошибка исходит от GTK +, неуправляемого кода, поэтому она не может быть перехвачена. У кого-нибудь есть какие-нибудь яркие идеи по поводу причины проблемы и как ее исправить? Или, если это не удастся, есть яркие идеи о том, как определить, какой из них следует вызывать во время выполнения?

Ответы [ 4 ]

8 голосов
/ 11 февраля 2009

Gtk + на Win32 не поддерживает многопоточность. Вы должны делать все ваши графические вызовы из того же потока, что и Gtk.Main ().

На самом деле это не так плохо, как кажется. Есть трюк, который вы можете использовать для отправки функций в основной поток. Просто используйте GLib.Idle.Add () из любого потока, и функция будет вскоре вызвана в том же потоке, в котором работает основной цикл. Просто не забудьте вернуть false в функцию-обработчик простоя, иначе она будет работать вечно.

Если вы следуете и используете вышеописанные методы, вам даже не нужно вызывать Gdk.Threads.Init ().

2 голосов
/ 09 февраля 2009

Я не уверен, что это работает на Mono, но вы можете использовать класс Environment, чтобы определить, на какой ОС работает программа.

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...

Перечисление PlatformID содержит больше, чем просто Windows и Unix, поэтому вам следует проверить другие значения.

1 голос
/ 09 февраля 2009

На самом деле, это может быть ошибка либо в привязках C # для GDK, либо в вашей версии GDK. В соответствии с документацией для gdk\_threads\_init(), g\_thread\_init() должен быть вызван в первую очередь, и документация GTK # говорит то же самое:

GLib.Thread.Init() должен быть вызван до Gdk.Threads.Init().

На моей машине с Linux (с GDK 2.14.4) программа на C, которая вызывает gdk\_threads\_init() без вызова g\_thread\_init(), печатает сообщение об ошибке и завершается с ошибкой. Удостоверились ли вы в том, что у вас одинаковая версия GDK для Linux и Windows, и что для версии Linux (если она отличается от версии для Windows) также требуется вызов g\_thread\_init() или если что-то изменилось две версии. Наконец, проверьте, что эта программа завершается с ошибкой:

#include <gdk/gdk.h>

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

    return 0;
}

Скомпилируйте его с помощью gcc -o test `pkg-config --cflags --libs gdk-2.0` test.c
(при условии, что вы сохранили его как test.c.)

Если эта программа завершается с ошибкой, это ошибка в вашей библиотеке GDK #.
Если это не так, это ошибка в вашей версии GDK.

0 голосов
/ 09 февраля 2009

Хмм, вы пытались украсить свой метод Main атрибутом [STAThread]?

например.

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

Если вы не можете использовать условную компиляцию, как это ...

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