【问题标题】:One way delphi thread communication? [closed]delphi线程通信的一种方式? [关闭]
【发布时间】:2015-02-28 11:03:09
【问题描述】:

假设我的 Delphi 程序有两个线程。

当然是主线程和运行线程(始终运行它永不停止)。

我想知道在运行线程中更新变量(16 个原子整数)而不产生问题的最佳方法是什么。

运行器线程包括始终通过 USB 发送低级数据(时间关键

我只需要一种方法,因为运行线程从不向主线程报告。

有什么想法吗?

谢谢!

【问题讨论】:

标签: multithreading delphi variables


【解决方案1】:

您的问题非常广泛。

有许多可用选项,最适合您的选项取决于您的特定需求和环境。您需要提供有关希望主线程在运行器线程上更新的数据类型的更多详细信息。预计多久更新一次?您已经说过运行器线程是时间关键的 - 这意味着您想要保持锁定(PS:多线程应用程序中减速的主要来源是不同线程竞争相同的锁。称为锁竞争。)到最低限度。 (正如我所说:这取决于您的需求。)

PS:必须使用主线程设置的所有数据吗?或者运行器线程是否可以简单地使用最新的可用数据而忽略可能已分配的许多中间值? 这个问题的答案产生了根本不同的选择


例如,原子更新可以在没有任何锁定的情况下执行。看看TThread.Terminated。这是一个简单的值。您不需要任何锁来更新此特定值。不存在有问题的竞争条件,因为处理器会将值作为整个原子单元读取或写入。

即使您同时更新 while not Terminated 循环正在读取该值 - 也不会有问题。更新要么发生在读取之前(导致线程退出循环),要么发生在之后(导致在下一次读取时循环退出之前再进行一次迭代)。

PS:请务必注意,设置字符串值不是原子操作。

现在您已经表明主线程根本不需要读取运行线程的数据。但我会用这种可能性作为对比的例子。如果您需要在 runner 线程上增加一个整数值,那需要保护。这是因为增加一个值是一个多步操作:

  • 读取值。
  • 对读取的值执行计算。
  • 写入一个新值。

如果运行线程和主线程同时使用该值,可能会得到不一致的结果。

另一种可能导致问题的情况是数据由许多相互交互的值组成。例如。 NoOfUnitsMassPerUnit 组合使用以确定TotalMass。独立更新这些值可能会导致竞争条件导致行为不一致。

Silver Warrior's answer 提供了一种保护多个值的技术。尽管请注意,该答案的当前版本存在一些严重错误。

请注意,如果您将数据封装在单独的对象中,则可以在没有任何锁的情况下更新运行器线程的数据,因为您可以原子地更新指针值。 (非常注意:您必须遵循许多特殊规则,并且您需要弄清楚如何避免内存泄漏......但这是更具体问题的详细信息。 )

另一种选择是将运行器线程实现为消息队列。 IE。当主线程想要更​​改值时,它会向运行线程发送一条消息。跑步者只会在“安全”的情况下处理更改后的值指令。 (同样,这是否可行取决于您的具体要求。)


作为最后一点,除了保护数据免受竞争条件之外,还有一些额外的问题。您的跑步者线程究竟时间有多紧迫?它做了多少处理?它是否需要快速响应某些事件?如果有,是什么事件?

这些问题的答案对于理解运行器线程主循环的理想结构非常重要。例如,一个“繁忙循环”(一个可能迭代而不做任何事情只是为了确保它永远不会暂停的循环)会使线程高度响应,但会使机器资源匮乏并使其整体变慢。相比之下,消息队列通常会运行一个循环处理消息,直到没有消息为止,然后将线程置于“等待状态”,直到收到下一条消息。

PS:另一个潜在的争用和减速源是内存管理器。如果您的主线程和运行线程都执行大量堆分配/解除分配,您可能会在您甚至没有明确编码的区域发生锁争用。

【讨论】:

  • 运行线程有一个
【解决方案2】:

如果您正在考虑公开正在运行的线程的一些变量,然后用您的主线程更改这些变量,请不要这样做!我是认真的。不要!

为什么不呢?因为您的主线程永远无法知道何时可以安全地更改这些变量,并且最终可能会在运行线程读取它们的同时更改它们。

因此,我会创建一个包含所有变量的记录,但该记录不在您的跑步者范围内。

然后我会确保对该记录中的数据所做的所有更改都受到关键部分的保护。这是为了确保只有一个线程同时访问该记录。

然后在您的运行线程中,我将从该记录中读取数据并制作本地副本(此副本在您的运行线程中)。现在记录的读取再次受到临界区的保护。

现在您可能会问自己,为什么我要从正在运行的线程中读取这些变量的数据。造成这种情况的主要原因是,您的运行线程是现在可以更新这些变量中的数据什么时候不可以更新的线程。

我将变量数据存储在记录中的原因是为了尽可能快地进入和离开临界区。
您会看到,当您使用临界区时,进入临界区将锁定您将在临界区内访问的所有变量。因此,如果您有多个这些过程可能需要一段时间,因为变量被一个一个锁定。但是,如果您将所有变量都存储在进入临界区的记录中,则会立即锁定整个记录。
离开临界区并解锁对变量的访问时也是如此。

【讨论】:

  • 只要您用一个临界区保护所有变量,它们是否都一起存在于一个记录中并不重要。变量在任何情况下都不会被锁定。
  • 锁定临界区会锁定临界区 - 不是单个变量。关键部分后面有多少值并不重要。临界区完全不知道您将要访问的变量。
  • 重申:您的最后一段完全错误。在您描述的场景中减少锁定时间的唯一方法是:(1) 锁定 (2) 替换指向记录的指针(或替换对象参考)。 (3) 解锁.....如果这就是您要解释的内容,则需要改写该部分。
  • 即使是原子变量?引用stackoverflow:如果线程A同时执行原子写入和线程B原子读取相同的数据,线程B读取的数据始终是一致的——不可能线程B读取的某些位是从当前写入操作和前一次写入的一些位(由线程 A)
  • 这个想法不是“锁定变量”。这个想法是 all 线程锁定代表任意数量变量的关键部分。只要你有一把锁,你就可以根据需要访问尽可能多的变量。但是,它们一次都锁定到一个线程(因此,即使您有 2 个变量,而线程 A 正在访问其中一个,线程 B 甚至无法访问另一个变量)。正如其他人所说,临界区完全不知道它负责锁定什么。
猜你喜欢
  • 2018-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-27
  • 1970-01-01
  • 1970-01-01
  • 2010-10-14
  • 1970-01-01
相关资源
最近更新 更多