为什么我不能在 WinRT(Windows Store App)中阻塞主线程?
Why can I not blocking main thread in WinRT(Windows Store App)?
这个问题与 "should I block my main thread" 无关,因为阻止 main/STA/UI 线程用于消息传递和 UI 操作通常不是一个好主意,但为什么 WinRT C++/cx 不这样做与 iOS、Android 甚至 C# 相比,t 不允许主线程的任何阻塞(虽然 await 实际上不会阻塞)。
Android 或 iOS 阻塞主线程的方式有根本区别吗?为什么 WinRT 是唯一不允许任何形式的阻塞同步的平台?
编辑:我知道 VS2015 中的共同等待,但由于向后兼容性,我的公司仍在使用 VS2013。
大话题,以极快的速度。这延续了很久以前在 COM 中开始的传统。 WinRT 继承了几乎所有相同的概念,它确实得到了相当大的清理。基本的设计考虑是线程安全是库设计中最困难的方面之一。并且任何库都有 class 从根本上讲线程不安全的元素,如果库的使用者没有意识到这一点,那么他很容易产生一个极难诊断的严重错误。
对于依赖闭源商业模式和 1-800 支持 phone 号码的公司来说,这是一个丑陋的问题。这样的 phone 调用可能 非常 不愉快,线程错误总是需要告诉程序员 "you can't do that, you'll have to rewrite your code"。很少有一个可以接受的答案,也不是 :)
所以线程安全不被视为程序员需要自己正确处理的事后想法。 WinRT class 明确指定它是否是线程安全的(ThreadingModel 属性),并且如果无论如何以不安全的方式使用它,应该如何使其成为线程安全的(MarshallingBehavior 属性)。主要是运行时细节,请注意编译器警告 C4451 如何使这些属性产生编译时诊断。
"used in an unsafe way anyway" 子句就是您要问的。 WinRT 可以使 class 本身不是线程安全的,但有 one 细节它自己无法弄清楚。为了使其安全,它需要知道创建 class 对象的线程是否可以支持操作系统提供的使对象安全的方法。如果线程没有,那么 OS 必须自己创建一个线程来给对象一个安全的家。解决了这个问题,但是效率很低,因为每个方法调用都必须编组。
你必须做出承诺,用你的心希望死的风格。如果您的线程解决了 producer-consumer problem,操作系统可以避免创建线程。在 Windows 白话中更广为人知的是 "pumping the message loop"。 OS 无法自行解决的问题,因为您通常要等到 在 创建了一个线程不安全的对象后才开始泵送。
你还承诺了一个承诺,即消费者不会阻塞并停止接受来自消息队列的消息。阻塞是不好的,隐含的是当消费者阻塞时工作线程无法继续。更糟糕的是,阻塞很可能导致死锁。当涉及 两个 同步对象时,线程问题始终是一个重大风险。一个被您阻止,另一个隐藏在等待调用完成的 OS 中。在看不到导致死锁的同步对象之一的状态时诊断死锁通常令人不快。
强调承诺,如果你违背承诺并阻止,OS将无能为力。它会让你,而且不一定是致命的。它通常不会也不会导致任何反应迟钝 UI。与在 CLR 上运行的托管代码不同,如果它阻塞,则 CLR 将进行抽取。大多数情况下都有效,但可能会导致一些令人困惑的重入错误。本机 C++ 中不存在该机制。死锁实际上 并不难诊断,但您必须找到正在等待 STA 线程恢复业务的线程。它的堆栈跟踪说明了这个故事。
使用 C++/CX 时请注意这些属性。除非您明确提供它们,否则您将创建一个始终被认为是线程安全的 class(ThreadingModel = Both,MarshallingType = Standard)。一个不经常实际测试的方面,它将是破坏该期望的客户端代码。好吧,你会接到一个 phone 电话,你必须给出一个令人不愉快的答案 :) 还要注意 OSX 和 Android 几乎不是运行时系统的唯一例子提供 WinRT 保证,.NET Framework 也不提供。
简而言之:因为 WinRT 应用程序的策略是 "thou shalt not block the UI thread" 并且 C++ PPL 运行时强制执行此策略,而 .NET 运行时不执行此策略——查看 ppltasks.h
并搜索 防止 Windows 运行时 STA 线程阻塞 UI。 (请注意,虽然 .NET 不强制执行此策略,但它会让您不小心陷入僵局)。
如果你必须阻塞线程,有一些方法可以使用 Win32 IPC 机制来实现(比如等待一个将由你的完成处理程序发出信号的事件)但是一般的指导仍然是 "don't do that" 因为它的用户体验很差。
这个问题与 "should I block my main thread" 无关,因为阻止 main/STA/UI 线程用于消息传递和 UI 操作通常不是一个好主意,但为什么 WinRT C++/cx 不这样做与 iOS、Android 甚至 C# 相比,t 不允许主线程的任何阻塞(虽然 await 实际上不会阻塞)。
Android 或 iOS 阻塞主线程的方式有根本区别吗?为什么 WinRT 是唯一不允许任何形式的阻塞同步的平台?
编辑:我知道 VS2015 中的共同等待,但由于向后兼容性,我的公司仍在使用 VS2013。
大话题,以极快的速度。这延续了很久以前在 COM 中开始的传统。 WinRT 继承了几乎所有相同的概念,它确实得到了相当大的清理。基本的设计考虑是线程安全是库设计中最困难的方面之一。并且任何库都有 class 从根本上讲线程不安全的元素,如果库的使用者没有意识到这一点,那么他很容易产生一个极难诊断的严重错误。
对于依赖闭源商业模式和 1-800 支持 phone 号码的公司来说,这是一个丑陋的问题。这样的 phone 调用可能 非常 不愉快,线程错误总是需要告诉程序员 "you can't do that, you'll have to rewrite your code"。很少有一个可以接受的答案,也不是 :)
所以线程安全不被视为程序员需要自己正确处理的事后想法。 WinRT class 明确指定它是否是线程安全的(ThreadingModel 属性),并且如果无论如何以不安全的方式使用它,应该如何使其成为线程安全的(MarshallingBehavior 属性)。主要是运行时细节,请注意编译器警告 C4451 如何使这些属性产生编译时诊断。
"used in an unsafe way anyway" 子句就是您要问的。 WinRT 可以使 class 本身不是线程安全的,但有 one 细节它自己无法弄清楚。为了使其安全,它需要知道创建 class 对象的线程是否可以支持操作系统提供的使对象安全的方法。如果线程没有,那么 OS 必须自己创建一个线程来给对象一个安全的家。解决了这个问题,但是效率很低,因为每个方法调用都必须编组。
你必须做出承诺,用你的心希望死的风格。如果您的线程解决了 producer-consumer problem,操作系统可以避免创建线程。在 Windows 白话中更广为人知的是 "pumping the message loop"。 OS 无法自行解决的问题,因为您通常要等到 在 创建了一个线程不安全的对象后才开始泵送。
你还承诺了一个承诺,即消费者不会阻塞并停止接受来自消息队列的消息。阻塞是不好的,隐含的是当消费者阻塞时工作线程无法继续。更糟糕的是,阻塞很可能导致死锁。当涉及 两个 同步对象时,线程问题始终是一个重大风险。一个被您阻止,另一个隐藏在等待调用完成的 OS 中。在看不到导致死锁的同步对象之一的状态时诊断死锁通常令人不快。
强调承诺,如果你违背承诺并阻止,OS将无能为力。它会让你,而且不一定是致命的。它通常不会也不会导致任何反应迟钝 UI。与在 CLR 上运行的托管代码不同,如果它阻塞,则 CLR 将进行抽取。大多数情况下都有效,但可能会导致一些令人困惑的重入错误。本机 C++ 中不存在该机制。死锁实际上 并不难诊断,但您必须找到正在等待 STA 线程恢复业务的线程。它的堆栈跟踪说明了这个故事。
使用 C++/CX 时请注意这些属性。除非您明确提供它们,否则您将创建一个始终被认为是线程安全的 class(ThreadingModel = Both,MarshallingType = Standard)。一个不经常实际测试的方面,它将是破坏该期望的客户端代码。好吧,你会接到一个 phone 电话,你必须给出一个令人不愉快的答案 :) 还要注意 OSX 和 Android 几乎不是运行时系统的唯一例子提供 WinRT 保证,.NET Framework 也不提供。
简而言之:因为 WinRT 应用程序的策略是 "thou shalt not block the UI thread" 并且 C++ PPL 运行时强制执行此策略,而 .NET 运行时不执行此策略——查看 ppltasks.h
并搜索 防止 Windows 运行时 STA 线程阻塞 UI。 (请注意,虽然 .NET 不强制执行此策略,但它会让您不小心陷入僵局)。
如果你必须阻塞线程,有一些方法可以使用 Win32 IPC 机制来实现(比如等待一个将由你的完成处理程序发出信号的事件)但是一般的指导仍然是 "don't do that" 因为它的用户体验很差。