如何使多线函数成为原子

Maw*_*awg 3 delphi

我有一个必须完全运行的功能,不会中断,并返回一个结果.如果异步事件导致在它仍在执行时再次调用它,则必须以某种方式阻止该调用,直到第一次调用完成.

互斥?别的什么?


[更新]该函数在我的主表单类中,并从类的两个方法调用:一个处理从serail端口读取的数据,另一个处理计时器到期.这两个似乎都在他们自己的线程中运行,因为一个函数的调用可以被另一个调用中断(对我来说,在应用程序级别,它们只是我在设计时放在主表单上的组件).

也许TCriticalSection?(但谷歌搜索不清楚我是否需​​要使用acquire/release或'进入/离开',似乎因为代码是一个简单的函数可能是mainform,它将是可重入的).

也许我要求的是一种使代码"不可重入"的方法,阻止直到第一次完成?无论答案是什么,我认为我将需要一个代码示例,或UTl到一个:-(

(请注意,此页面提供了大量有用的信息(我不会全部了解,但它对其他人来说非常有用))


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

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

Ari*_*The 7

将函数移动到没有异步事件的单独工作线程.

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


有更多信息的更新.假设(猜测)这些函数通过RS232或者什么都没有接收数据,并且正在做类似GUI更新的事情,我将草拟以下方法.

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

更多细节:

  1. 为WM_USER grep VCL源代码并查看模式.您声明了3个消息ID常量:Work_Complete,Work_Request,Work_EmptyQueue.形式上的通信消息方法.
  2. 我将使用OmniThreadLibrary中的准备好的线程安全队列.但是你可以继承System.Contnrs.TQueue或System.Generics.Collections.TQueue,将所有数据传递方法包装成关键部分.总的来说,如果他们可以为您提供服务,我建议您去OmniThreadLibrary管道
  3. 这是硬件数据工作应用程序的标准方式.是它的MS-DOS设备驱动程序.或者一些嵌入式设备.您应该将快速和精益数据保存与长时间工作分开.

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

定时器甚至会使空数据包(零长度字节数组)并将其排队.如果该计时器甚至还有一些数据要通过 - 那么它应该是变量记录或单独的输入队列或其他什么.但没有信息似乎计时器只发送信息,但警报本身

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

在将工作负载排入队列后(例如在输入队列的通知事件中),我将执行win32 PostMessage(MainForm.Handle,work_request).毕竟我们想在一个地方集中线程控制.要保持线程隔离消息应该发布,没有SendMessage没有TControl.Perform!

在表单的work_request处理程序的主线程中,我将查看输入队列是否已经为空.如果没有,那么我会查看工作线程状态.如果它被暂停,我会恢复它.

工作线程看起来是输入队列有什么东西,而它有,它会:

  1. 将队列中的数据包提取到本地var中
  2. 做一些可能很长的处理
  3. 输出"工作结果"包.假设GUI更新可能只是键值对的集合,或者是一些数据字段的记录,其中数据字段被填充并且要被忽略.
  4. 将该数据包排入输出队列(队列然后将Work_Complete msg发布到主表单)
  5. 循环到1.

如果输入队列为空,则该函数退出,该线程执行PostMessage Work_EmptyQueue并暂停,直到稍后将被唤醒以执行更多工作或释放.

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

  1. 从输出队列中提取所有数据包到本地变量
  2. 合并他们.(如果我有p1 =(label1 ='aaaa',label2 ='bbbb')和p2 =(label3 ='cccc',label1 ='dddd')则累计任务将被设置(label1 ='dddd',label2 ='bbbb',label3 ='cccc');
  3. 应用它们(实际更新GUI)

如果输出队列为空,则省略步骤2和3(如果在前一步骤中发生合并,则将省略).但不是第四.步骤1和2是分开的.由于队列操作是线程互锁的,因此步骤1的目标是尽可能快地提取数据.Margine将在当地完成.

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

这是一个粗略的草图.它可以更好地雕刻给你某些cituation.


Dav*_*nan 7

假设您担心来自同一线程的重入,那么阻止重入调用将导致典型的死锁.如果这是您的方案,那么您需要执行以下操作之一:

  1. 确保无法进行重入调用,或
  2. 检测重入调用并通过添加到队列以便稍后处理来推迟它,或者
  3. 检测重入呼叫并忽略它.

很可能这些选项都没有吸引你!

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


Sea*_*kin 5

使用两种机制的组合:

  1. 使用TCriticalSection防止其他线程进入.显然这只是必要的多线程应用程序.
  2. 使用再入门.这是一个简单的布尔值,可以防止从同一个线程重新进入.如果已输入您要保护的功能,则不要阻止,而是反弹."弹跳"意味着采取一些适当的操作,例如排队所需的活动以便以后执行.