在GI-Gtk中使用回调时遇到线程问题。请考虑以下程序:
import GI.Gtk hiding (main)
import GI.Gdk
import GI.GLib
import GI.GLib.Constants
import qualified GI.Gtk as Gtk
import Control.Concurrent
import Control.Concurrent.MVar
main = do
t <- myThreadId
putStrLn $ "init "++show t
Gtk.init Nothing
d <- dialogNew
dialogRun d
onWidgetDestroy d onDestroy
Gtk.main
onDestroy :: IO ()
onDestroy = do
t <- myThreadId
putStrLn $ "onDestroy "++show t
runOnGUI $ putStrLn "Destroying"
putStrLn "Destroyed"
Gtk.mainQuit
runOnGUI :: IO a -> IO a
runOnGUI action = do
ans <- newEmptyMVar
threadsAddIdle PRIORITY_DEFAULT_IDLE $ action >>= putMVar ans >> return False
takeMVar ans
运行上述程序时,程序在我尝试关闭窗口时挂起,并得到以下输出:
init ThreadId 11
Gtk-Message: GtkDialog mapped without a transient parent. This is discouraged.
onDestroy ThreadId 12
单独查看代码,如果从主线程调用runOnGUI(我希望它是这样),我希望看到这个死锁。但是根据输出,应该在不同的线程上调用runOnGUI,从而避免死锁。
在我的实际程序中,我试图通过在if currentThread /= mainThread
类型条件中保护runOnGUI来避免这种类型的死锁;但是,正如上面的例子所示,这似乎不够。