首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使多行函数原子化

如何使多行函数原子化
EN

Stack Overflow用户
提问于 2012-08-15 13:42:14
回答 4查看 684关注 0票数 3

我有一个函数,必须完全运行,不受干扰,并返回结果。如果异步事件导致它在仍在执行时再次被调用,则必须以某种方式阻止该调用,直到第一次调用完成为止。

穆特克斯?还有别的吗?

Update函数位于我的主表单类中,并通过类的两种方法调用:一种是处理从serail端口读取的数据,另一种是句柄计时器过期。这两种方法似乎都在它们自己的线程中运行,因为一个函数的调用可以由另一个调用来交互(对我来说,在应用程序级别上,它们只是我在设计时放在主表单上的组件)。

也许是TCriticalSection?(但是googling使我不清楚我是需要使用acquire/release还是使用‘enter/ need’,而且由于代码具有可能主要形式的简单功能,所以它将是可重入的)。

也许我想要的是一种使代码“不可重入”的方法,阻塞直到严格的入口完成为止?不管答案是什么,我想我需要一个代码检查器,或者一个UTl

(请注意,此页提供了许多有用的信息(我并不是全部摸索,但对其他人来说却非常有用))

更新这与GUI更新无关,只是发送一些TCOP数据的每个事件都应该重新接收响应。

虽然该应用程序有一个GUI (它是基于表单的),但这只是为了防止我想显示一些调试信息,因为应用程序将在没有监视器的PC上运行(是的,我知道它仍然有GUI,但这不是我的问题/要点)。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2012-08-16 17:37:57

另一种方法是当从一个事件源(例如,串口处理程序)输入关键函数时,在调用期间禁用另一个事件源(过期计时器)。打完电话后再重新启用。您可以保证其他事件源在调用期间不会触发,因为计时器已被禁用。

对于即将处理串行数据包时关闭过期计时器的情况,这是很简单的。如果假设过期计时器是跟踪从串行端口接收到的任何时间,则禁用对接收的串行数据的过期计时器是合适的。在处理完串行数据包时恢复/重新启动过期计时器。

相反的情况--在处理过期计时器时接收串行数据--可能要复杂一些,因为您可能不想通过禁用串行数据接收器来冒丢失数据的风险。如果您的串行数据处理程序可以很容易地暂停/恢复而不会丢失数据,那么您就完成了!否则,您最好的选择可能是在对象实例中使用布尔标志来指示关键函数何时执行。

如果一个串行数据包进入并且该标志为真,则将该串行数据包推到一个列表中,以便稍后处理并立即返回。设置过期计时器处理程序,在从关键调用返回之前和之后立即检查该列表。如果列表是非空的,则处理列表中的所有项.如果当您输入过期计时器处理程序时,列表中有等待处理的数据包,则可能要忽略过期计时器过期并继续执行。

在串行数据处理程序中,在调用关键函数之前和之后检查列表是否为非空。检查之前将有助于确保按接收顺序处理串行数据包。检查后将确保在处理当前数据包期间到达的后续串行数据不会丢失。

如果在不同的线程上调用串行数据事件处理程序的可能性很大,则应该以线程安全的方式小心处理布尔标志和列表的更新。如果串行事件处理程序(和过期计时器处理程序)保证在UI线程上执行,那么您不必担心这一点。

票数 1
EN

Stack Overflow用户

发布于 2012-08-15 13:48:22

将函数移动到不存在异步事件的单独工作线程。

主线程中的异步事件应该将一些消息发布到某个队列中,因此当工作线程完成执行时,主线程将使用新的参数再次启动它。

有一些更多的信息更新。假设(猜测)这些函数对RS232上接收到的数据起作用,或者完全不起作用,并且正在执行类似于GUI更新的操作,我将概述以下方法。

  1. 使用窗体的Windows消息传递队列进行警报。
  2. 使用一些联锁队列对象进行数据传递。(是的,我知道我可以在Windows消息中添加指针,但我想要更多的类型安全性)
  3. 使用外部工作线程进行长时间处理。

更多详细信息:

  1. WM_USER的grep源代码,并查看模式。声明3个消息id常量: Work_Complete、Work_Request、Work_EmptyQueue。和相应的信息-方法的形式。
  2. 我会使用来自OmniThreadLibrary的就绪线程安全队列。但是您可以子类System.Contnrs.TQueue或System.Generics.Collections.TQueue,将它们的所有数据传递方法包装到关键部分。总的来说,我建议你到OmniThreadLibrary管道上去厕所,如果他们能帮你
  3. 这是硬件数据工作应用程序的标准方式。是MS设备驱动程序。或者某种嵌入式设备。您应该将快速和精益的数据保存与长期工作分开。

因此,RS232事件处理程序将读取COM中的数据,将其放入TBytes,然后将该数据包添加到输入TQueue。更复杂的方法是查看队列是否已经包含来自COM的数据,并使用旧包聚合新数据包,而不是将新数据包作为单独的包。这需要更仔细的锁定,所以在这里聚集可能不值得蜡烛。

定时器甚至会使空包(零长度字节数组),并对它进行类似的排队。如果那个定时器也有一些数据要传递,那么它应该是变体记录,或者是独立的输入队列等等。但是如果没有信息的话,定时器就不会发送任何信息,只会发送警报本身。

根据您的话,Timer和RS232事件都在各自的线程中工作。我对此有疑问,但我必须相信你。

在对工作负载进行排队之后(例如,在输入队列的通知事件中),我将执行win32 PostMessage(MainForm.Handle,work_request)。毕竟,我们希望将线程控制集中在一个地方。为了保持线程隔离的消息应该被张贴,没有SendMessage没有TControl.Perform!

在表单的work_request处理程序中的主线程中,我将查看输入队列是否已经为空。如果没有,那么我将查看辅助线程状态。如果它被暂停了,我会继续它。

工作线程将查找的是输入队列有一些内容,虽然它有,但它会:

  1. 将数据包从队列中提取到本地var中。
  2. 做一些可能很长的处理
  3. 制作输出“工作结果”包。假设这是GUI更新,这可能只是键值对的集合,或者数据字段的一些记录,其中包含填充了哪些数据字段,哪些数据字段将被忽略。
  4. 将该数据包排队到输出队列中(队列然后将Work_Complete消息发送到主表单)
  5. 循环到1。

如果输入队列为空,则函数退出,线程执行PostMessage Work_EmptyQueue并将自身挂起,直到稍后被唤醒以完成更多的工作或释放。

当MainForm (再次在主线程中)接收到Work_Complete时

  1. 将输出队列中的所有数据包提取到本地vars中。
  2. 合并他们。(如果我有p1=(label3 1=‘aaaa’,label3 2=‘bbbb’)和p2=(label3 3=‘cccc’,label3 1=‘dddd’),那么累积的任务将是设置(label3 1=‘dddd’,label3 2=‘bbbb’,label3 3=‘cccc’);
  3. 应用它们(实际上是更新GUI)

如果输出队列为空,则省略步骤2和步骤3(如果合并发生在前一步中)。但不是第四。步骤1和步骤2是分开的。由于队列操作是线程联锁的,第1步的目标是尽可能快地提取数据。马吉娜要在当地做。

当MainForm接收到Work_EmptyQueue时,它会检查输入队列现在是否为空,并可能恢复工作线程。它还可以选择对状态进行GUI更新或其他任何操作。

这是一个粗略的草图。它可以雕刻得更好,对你一定的要领。

票数 7
EN

Stack Overflow用户

发布于 2012-08-15 13:49:00

假设您关注同一线程的可重入性,那么阻塞重入者调用将导致典型的死锁。如果这是您的场景,那么您需要执行以下操作之一:

  1. 确保不发生重入者调用,或者
  2. 检测重入调用,并通过添加队列以供以后处理来延迟它,或
  3. 检测到一个可重入的呼叫,然后忽略它。

很可能这些选择对你都没有吸引力!

如果您关心来自不同线程的同时调用,那么您可以使用某种类型的锁。例如,在Windows上,您通常会使用关键部分。

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11970372

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档