【问题标题】:How is CopyFileEx's pbCancel parameter safe?CopyFileEx 的 pbCancel 参数如何安全?
【发布时间】:2021-03-04 20:04:00
【问题描述】:

发布了几个问题(例如Send flag to cancel CopyFileEx after it has started),这些问题引用了使用 Win32 的CopyFileEx() 函数的pbCancel 参数来取消正在进行的复制的能力。我不清楚的是,为什么在没有任何同步机制(互斥锁等)的情况下在另一个线程中设置该布尔值是安全的?只有当另一个线程将该布尔值设置为 true 时,此功能才真正有用,因为 CopyFileEx() 会阻塞,直到文件复制完成。

这是否依赖于 Windows 平台的特性?

【问题讨论】:

  • 文档中没有提到另一个线程。取消操作的主要方法是通过进度回调。您可以从该回调中执行您需要的任何操作,包括读取从不同线程修改的值。没有什么能阻止您根据需要实施任何类型的同步或内存屏障。
  • 啊,我明白了。我没有意识到回调在与 copyfileex() 相同的线程中运行。在互联网研究期间,我发现有人在谈论从另一个线程设置它,我认为这并不安全。如果您想发表评论作为答案,我可以接受。
  • 这里有什么不安全的设置 boolean (在意义上 0 或不是 0)变量来自另一个线程?这里当然不需要任何同步

标签: winapi thread-safety


【解决方案1】:

如果简单地通过感知变量(可以为 0 或不为 0)设置布尔值,而与其他数据没有任何联系 - 不需要任何同步 - 为什么?! 一个线程将变量设置为真,另一个线程从变量中读取或读取 0 或不读取 0。即使您确实在临界区写入和读取变量 - 这会发生什么变化?没有 !将被读取的线程 - 无论如何从变量中加载或加载 0 或不加载 0。

在另一种情况下需要同步。通常,当我们写入另一个内存位置时,在将 true 存储在变量中之前,并希望在另一个线程从变量中读取 true 之后 - 所有其他内存修改都已经可见。但万一取消回调 - 没有任何其他数据

或者,如果我们将复杂数据写入变量(不仅或 0 或非 0)并且此写入不是原子的 - 同步需要防止读取部分状态。但这里不可能有任何设计的“部分状态”


对于不懂的人:

无论是写入或读取 pbCancel 都是原子的。 在任何情况下,最终都会从 pbCancel 中读取一些值。

如果在复制操作期间此值将interpreted 作为TRUE,则取消操作。 否则,复制操作将继续完成。

如果在复制操作期间将此标志设置为 TRUE,则取消操作。否则,复制操作将继续 完成。

即使会读取一些“瞬态状态” - 等等 - 在 if/else 表达式中以任何方式使用此值,结果复制操作将被取消或继续。

没有指定(这与常识相矛盾)将执行对 0 (false) 和 1(true) 的严格检查,并且在任何情况下其他值会有异常或UB。

相反——明确指出否则(即flag不设置为TRUE),复制操作将继续完成。。没有任何关于异常、UB等的说法

如果查看CopyFileExW 的声明,这里可以更详细地了解 pbCancel 值是如何解释的:

_When_(pbCancel != NULL, _Pre_satisfies_(*pbCancel == FALSE))
_Inout_opt_ LPBOOL pbCancel,

所以检查是(这是最原生的)

if (pbCancel == NULL || *pbCancel == FALSE)
    // continue copy
else
    // cancel copy

这里没有任何“瞬态”。这里或 0 或不是 0。即使您将 1 写入 pbCancel 但另一个线程从中读取到 0x5FD38FCA - 这将被解释为 TRUE并且复制操作将被取消。

无论如何-如果您将true(严格意义上的1)写入变量-另一个线程迟早会从该变量中读取1。在关键部分执行此操作 - 没有任何变化 - 只是迟早另一个线程会读取此值。不是更快。

【讨论】:

  • 这是不正确的。甚至对于像 x86 这样宽容的架构也不行。从一个线程通过指针写入BOOL 仍然可以让另一个线程观察瞬态值。简单的反例:跨越高速缓存行边界的BOOL
  • @IInspectable - 你什么都不懂。什么是瞬态值??或者它将是 0 或不是 0。这不取决于任何 cpu 架构。我的回答是正确的。你的评论是错误的
  • 也许我错了。或者你可能是,就像demonstrated here。我相信你仍然相信这个答案是完全正确的,而它可证明是错误的。这不是我能解决的。您需要了解 CPU 真正是如何工作的,以及编程语言如何通过在抽象机器上指定内存模型来捕捉这种复杂性。
  • @IInspectable - 再次 - 这绝对不取决于 CPU 的实际工作方式等。你不能或特别不想理解非常简单的事情 - 从变量读取的线程 - 读取一些值。此值可以为 0。也可以不为 0。all.if 不为 0 - 复制文件中断。如果为 0 - 继续。这里有什么问题?
  • 也许我太厚了,无法理解“非常简单的事情”。或者,也许你让这件事听起来比实际简单得多。现在,回到最初的问题:如果BOOL 变量跨越缓存行边界会发生什么?更新该值需要多少内存读取和内存写入?如果不止一个,那么对存储值进行原子更新的可能性有多大?
猜你喜欢
  • 2018-08-24
  • 1970-01-01
  • 2015-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-12
  • 1970-01-01
  • 2011-11-25
相关资源
最近更新 更多