继续OS X - 两个库调用系统函数

时间:2016-01-11 15:16:56

标签: multithreading macos user-interface go goroutine

我发现至少对Mac来说,用或多或少的常用UI编写内容很困难。我的申请必须tray icon,并且能够显示system notifications

问题是goroutines本身。在Mac上对UI框架的任何调用都要求从主线程调用,或者至少以线程安全的方式调用。

当我已经在运行UI时,问题出现了(好吧,对于GUI应用程序是必须的,不是吗?)并尝试显示通知。原因似乎是systray包Init函数必须使用runtime.LockOsThread锁定到主线程,并且永远不会释放它。然后,如果我尝试显示也需要runtime.LockOsThread的通知,则会导致以下错误:

2016-01-11 22:56:27.973 main[30162:4094392] *** Assertion failure in +[NSUndoManager _endTopLevelGroupings], /Library/Caches/com.apple.xbs/Sources/Foundation/Foundation-1256.1/Misc.subproj/NSUndoManager.m:359
2016-01-11 22:56:27.974 main[30162:4094392] +[NSUndoManager(NSInternal) _endTopLevelGroupings] is only safe to invoke on the main thread.
2016-01-11 22:56:27.977 main[30162:4094392] (
    0   CoreFoundation                      0x00007fff8d42bae2 __exceptionPreprocess + 178
    1   libobjc.A.dylib                     0x00007fff8bb03f7e objc_exception_throw + 48
    2   CoreFoundation                      0x00007fff8d42b8ba +[NSException raise:format:arguments:] + 106
    3   Foundation                          0x00007fff8cb4c88c -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 198
    4   Foundation                          0x00007fff8cad24c1 +[NSUndoManager(NSPrivate) _endTopLevelGroupings] + 170
    5   AppKit                              0x00007fff8514206a -[NSApplication run] + 844
    6   main                                0x0000000004166200 nativeLoop + 128
    7   main                                0x0000000004165bca _cgo_8c6479959095_Cfunc_nativeLoop + 26
    8   main                                0x000000000405a590 runtime.asmcgocall + 112
)
2016-01-11 22:56:27.977 main[30162:4094392] *** Assertion failure in +[NSUndoManager _endTopLevelGroupings], /Library/Caches/com.apple.xbs/Sources/Foundation/Foundation-1256.1/Misc.subproj/NSUndoManager.m:359
2016-01-11 22:56:27.978 main[30162:4094392] An uncaught exception was raised
2016-01-11 22:56:27.978 main[30162:4094392] +[NSUndoManager(NSInternal) _endTopLevelGroupings] is only safe to invoke on the main thread.

有解决方法吗?到目前为止,我所能想到的是将UI和Notifications放入单独的二进制文件中,并使它们通过某种IPC与main进行通信。但我可能会遗漏一些东西。

2 个答案:

答案 0 :(得分:2)

由于这个问题没有足够的牵引力,我决定发布我在尝试解决此问题时找到的解决方案。这不会被标记为答案,因为其他人可能会提供更好的解决方案。

我已将其中一个UI进程(即使用systray的部分)移动到另一个二进制文件中并使用cmd := exec.Command(...)cmd.Start()调用它然后我管道stdinstdout并通过这些进程与这个子进程沟通。

示例代码可以在Github找到。 警告此gist中出现错误,其中子进程主进程将在CPU周期内开始刻录。随意修复它。

我之所以不想使用RPC,是因为这会使我想要实现的内容变得有点过于复杂,并且无法提供简单的双向通信方式。

答案 1 :(得分:1)

看起来你正在使用的两个库都正确地使用runtime.LockOSThread来进行仅主线程的API调用;不幸的是,要使用多个这样的库,你必须做一些比提供的示例代码更漂亮的东西。您需要编写自己的主线程/ main.Main - 调用消息循环来处理对多个MTO API的调用。

runtime.LockOSThread是使用此类API进行操作的解决方案的一部分; golang wiki has a page关于如何使用它与“仅从主线程调用”API进行交互。

对程序应如何更改的简短描述:

您需要在main.init中使用runtime.LockOSThread来确保主线程正在运行main.Main; main.Main应该重构为两部分:

  1. 启动一个goroutine或goroutines,运行以前main.Main中的内容;
  2. 进入消息循环,接收消息以在一个或多个通道上执行某些主线程操作