为什么在主线程上调用东西是程序员的责任?

Why is it the programmer’s responsibility to call things on the main thread?

为什么程序员有责任在主线程上调用UI相关方法:

DispatchQueue.main.async {}

理论上,这不能留给编译器或其他代理来确定吗?

只有在极少数情况下,UI 才会调用主线程中的任何内容,但为了安全起见,用户登录超时除外。任何特定 window 的大多数 UI 相关方法都在初始化 window 时启动的线程中调用。 我宁愿管理我的 UI 调用而不是编译器,因为作为开发人员,我想要控制并且不想依赖第三方 'black boxes'.

你会用一种挫折代替另一种。

假设所有需要在主线程上调用的 UI 相关方法是通过以下方式完成的:

  • 使用 DispatchQueue.main.async:您将隐藏异步行为,没有明显的方法 "follow up" 结果。这样的代码现在会失败:

    label.text = "new value"
    assert(label.text == "new value")
    

    您可能会认为 属性 text 只是无害地分配了一些值。事实上,它将一个工作项排入队列以在主线程上异步执行。这样一来,您就打破了在完成该行时系统已达到所需状态的预期。

  • 使用DispatchQueue.main.sync:你会隐藏潜在的死锁。 main 队列上的同步代码可能非常危险,因为很容易无意中阻塞(在主线程上)自己等待这样的工作,从而导致死锁。

我认为实现这一目标的一种方法是有一个专用于 UI 的隐藏线程。所有 UI 相关的 API 都会切换到该线程来完成它们的工作。虽然我不知道这会有多昂贵(每次切换到该线程可能并不比等待锁快),但我可以想象有很多 "fun" 方法可以让你编写死锁代码.

检查https://developer.apple.com/documentation/code_diagnostics/main_thread_checker

仅从主线程更新UI!!!

实际答案是开发者惯性和g运行dfathering。

CocoaUIAPI是巨大的——不,是巨大的。自 1990 年代以来,它也一直在不断发展。

回到我年轻的时候,没有多核、64 位、任何东西,所有应用程序的 99.999% 运行 都在主线程上。时期。 (原来的MacOS,pre-OSX,连线程都没有。)

稍后,一些专门的任务可能 运行 在后台线程上,但大部分应用程序仍然 运行 在主线程上。

快进到今天,为后台执行分派数千个任务是微不足道的,CPU 可以 运行 30 个或更多当前线程,这很容易说 "hey, why doesn't the compiler/API/OS handle this main-thread thing for me?" 但近乎不可能的是重新-设计了四个十年的 Cocoa 代码和应用程​​序来实现它。

我要说的是,有数亿行代码都假定 UI 调用在主线程上并发执行。正如其他人指出的那样,没有切肉刀开关或预处理器可以撤销所有这些假设,修复所有这些潜在的死锁等。

(哎呀,如果编译器能解决这类问题,我们甚至不必编写多线程代码;您只需让编译器分割您的代码,以便 运行s同时。)

最后,这样的改变是不值得的。我全职 Cocoa 开发,我必须处理 "update control from a background thread problem" 的次数最多,每周一次左右。没有开发成本效益分析会花费一百万工时来解决已经有直接解决方案的问题。

现在,如果您要从头开发一个新的、现代的 UI API,您只需让整个 UI 框架线程安全,整个问题就迎刃而解了。也许 Apple 在某个地方的实验室中有一个 b运行d 新的、从头开始重新设计的 UI 框架可以做到这一点。但这是我看到这种情况发生的唯一方式。