我发现至少对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进行通信。但我可能会遗漏一些东西。
答案 0 :(得分:2)
由于这个问题没有足够的牵引力,我决定发布我在尝试解决此问题时找到的解决方案。这不会被标记为答案,因为其他人可能会提供更好的解决方案。
我已将其中一个UI进程(即使用systray的部分)移动到另一个二进制文件中并使用cmd := exec.Command(...)
和cmd.Start()
调用它然后我管道stdin
和stdout
并通过这些进程与这个子进程沟通。
示例代码可以在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
应该重构为两部分:
main.Main
中的内容;