【问题标题】:Why can I not blocking main thread in WinRT(Windows Store App)?为什么我不能阻止 WinRT(Windows Store App)中的主线程?
【发布时间】:2017-03-17 19:00:41
【问题描述】:

这个问题不是关于“我应该阻塞我的主线程吗”,因为阻塞主线程/STA/UI 线程通常是一个坏主意——用于消息传递和 UI 操作,但为什么 WinRT C++/cx 不允许任何与 iOS、Android 甚至 C# 相比,主线程的阻塞(await 实际上并没有阻塞)。

Android 或 iOS 阻塞主线程的方式有根本区别吗?为什么 WinRT 是唯一不允许任何形式的阻塞同步的平台?

编辑:我知道 VS2015 中的 co-await,但由于向后兼容性,我的公司仍然使用 VS2013。

【问题讨论】:

  • @RawN UI 线程或 COM 上下文中的 STA 线程。在 WinRT 中,UI 控件只能通过 UI 线程进行交互。
  • 在我看来,如果你知道这是一个坏主意,真正的问题不是为什么 Windows 不允许你这样做,而是为什么 iOS 和 Android 允许这样做!

标签: multithreading windows-runtime windows-store-apps c++-cx


【解决方案1】:

主题大,速度快。这延续了很久以前在 COM 中开始的传统。 WinRT 继承了所有相同的概念,它确实得到了相当大的清理。基本的设计考虑是线程安全是库设计中最困难的方面之一。并且任何库都具有基本上线程不安全的类,如果库的使用者没有意识到这一点,那么他很容易创建一个非常难以诊断的讨厌的错误。

对于依赖闭源商业模式和 1-800 支持电话号码的公司来说,这是一个丑陋的问题。这样的电话可能非常不愉快,线程错误总是需要告诉程序员“你不能这样做,你必须重写你的代码”。很少有一个可以接受的答案,也不是这样:)

因此,线程安全不被视为程序员需要自己解决的事后的想法。 WinRT 类明确指定它是否是线程安全的(ThreadingModel 属性),并且如果它以不安全的方式使用,应该如何使其成为线程安全的(MarshallingBehavior 属性)。主要是运行时细节,请注意编译器警告 C4451 甚至可以使这些属性产生编译时诊断。

“无论如何以不安全的方式使用”子句就是您要询问的内容。 WinRT 可以创建一个本身不是线程安全的类,但有一个它自己无法弄清楚的细节。为了使其安全,它需要知道创建类对象的线程是否可以支持操作系统提供的使对象安全的方法。如果线程没有,那么操作系统必须自己创建一个线程来为对象提供一个安全的家。解决了这个问题,但是效率很低,因为每个方法调用都必须编组。

你必须做出一个承诺,跨越你的心,希望到死的风格。如果您的线程解决了producer-consumer problem,操作系统可以避免创建线程。在 Windows 白话中更广为人知的是“泵送消息循环”。操作系统无法自行解决的问题,因为您通常要等到 创建了一个线程不安全的对象后才会开始抽水。

您再做出一个承诺,您还承诺消费者不会阻塞并停止接受来自消息队列的消息。阻塞是不好的,隐含的是当消费者阻塞时工作线程无法继续。更糟糕的是,阻塞很可能导致死锁。当涉及两个同步对象时,线程问题总是一个重大风险。您阻止的一个,另一个隐藏在等待调用完成的操作系统中。当您看不到导致死锁的同步对象之一的状态时诊断死锁通常是不愉快的。

强调promise,如果你违背承诺并阻止,操作系统将无能为力。它会让你,它不一定是致命的。它通常不会也不会导致用户界面无响应。在 CLR 上运行的托管代码不同,如果它阻塞,则 CLR 将泵送。大多数情况下都有效,但可能会导致一些非常令人困惑的重入错误。该机制在本机 C++ 中不存在。死锁实际上并没有那么难以诊断,但您必须找到等待 STA 线程恢复工作的线程。它的堆栈跟踪说明了这个故事。

使用 C++/CX 时请注意这些属性。除非您明确提供它们,否则您将创建一个始终被认为是线程安全的类(ThreadingModel = Both,MarshallingType = Standard)。一个不经常被实际测试的方面,它将是破坏这种期望的客户端代码。好吧,你会接到一个电话,你必须给出一个不愉快的答案 :) 另请注意,OSX 和 Android 并不是唯一不提供 WinRT 保证的运行时系统示例,.NET Framework 也不提供。

【讨论】:

  • “一个不经常实际测试的方面,它将破坏这种期望的客户端代码。好吧,你会接到一个电话,你必须给出一个不愉快的答案”这是由于我正在考虑阻塞 UI 线程,我们 99% 的客户无法处理异步/并发,我们希望提供他们不必同步的查看器的非异步版本。
  • 你不能忽视“无响应的 UI”问题。如果你不给他们一种异步使用你的代码的方法,那么他们会给你一个电话,而你根本没有答案。以 WinRT 为目标的程序员通常对 async/await 的使用非常了解,WinRT 本身需要像打开文件这样基本的东西。
  • @legokangpalla:除了作为 Windows 运行时的标准之外,异步调用可以简单地被包装以使它们同步。没有简单的方法可以使同步调用异步。异步接口更可取,因为它允许您的客户同时拥有它。
  • @HansPassant 如果我对此事有任何说法,我会坚持使用异步方法,实际上不提供任何非异步或提供非异步,但要理解用户将其包装在一个异步方法。但是,唉,我的前任用计时器等同步实现了它,我的老板希望 API 尽可能与 iOS 平台相似,并非常明确地告诉我,许多开发人员将无法正确使用异步方法,这在某种程度上是正确的我在韩国遇到的许多 winRT 开发人员都在为基于任务的并发或任何类型的并发而苦恼。
  • @IInspectable 遗憾的是,我的公司假设客户更喜欢我们库中的同步方法。我可以看到一些从 WPF 背景进入 WinRT 的 C# 开发人员对 async/future/await 没有任何问题。但管理在与其他移动平台类似的同步 API 上是死板的。
【解决方案2】:

简而言之:因为 WinRT 应用程序的策略是“您不应阻塞 UI 线程”,而 C++ PPL 运行时会强制执行此策略,而 .NET 运行时不会 - 查看ppltasks.h 并搜索 防止 Windows 运行时 STA 线程阻塞 UI。 (请注意,尽管 .NET 不强制执行此策略,但它会让您不小心死锁自己)。

如果您必须阻塞线程,有一些方法可以使用 Win32 IPC 机制(例如等待完成处理程序发出信号的事件),但一般指导仍然是“不要这样做”,因为它的用户体验很差。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-26
    • 1970-01-01
    • 2021-10-19
    • 1970-01-01
    • 2021-11-12
    • 2020-01-02
    相关资源
    最近更新 更多