VB.NET同步混乱
VB.NET、.NET 4
大家好,
我有一个控制工业系统的应用程序。它有一个 GUI,一旦进程启动,它主要显示各种连接设备的状态。它基本上是这样工作的:
- System.Timers.Timer 对象始终运行。在每个 Elapsed 事件中,它会轮询设备的当前值,并调用 GUI 上的控件,用新值更新它们。
- 单击开始按钮,创建并启动处理时间秒表对象(现在,除了在此事件上处理的其他工作外,GUI 上的标签还可以在 System.Timers.Timer 的 Elapsed 事件上调用和更新)
- 创建一个运行 Process() 子例程的新线程
- 创建并启动一些秒表对象(这些秒表在进程期间通过其 Restart() 方法定期重新启动。
- 在新秒表的 Elapsedmilliseconds 属性上执行一些逻辑,以确定何时执行诸如向设备写入新设定值、更新数据日志等操作...
这是我的问题:程序偶尔会冻结,我在追踪问题时的无知努力使我怀疑对设备子集的读/写操作。大多数情况下,RS-232 控制的罪魁祸首是罪魁祸首。但是,我偶尔会在程序冻结时看到其他奇怪的事情,例如,其 Text 属性由秒表的 Elapsedmilliseconds 属性确定的时间标签之一有时会显示一个不可能的值(例如,-50 小时或其他)。
对于 RS-232 问题,我怀疑诸如读取事件之类的事情与写入事件同时执行,这会导致冻结(?)。我试图通过确保与 RS-232 设备的所有通信都通过具有以下属性的 Transmit() 子例程来防止这种情况:
据我的无知允许我理解,这应该强制一个 Transmit() 执行在另一个 Transmit() 执行开始之前完全完成。也许另一个风险是如果一个 Transmit() 从未完成,代码会被阻塞?
关于秒表问题,我推测问题在于计时器试图在执行秒表的 Restart() 方法的同时更新 GUI 标签。我不确定这是否会导致问题。我所知道的是,这个问题仅发生在进程中调用 Restart() 时。
我想知道在更新标签时(或者相反,在重新启动标签时)是否可以使用 SyncLock 或其他东西来锁定秒表?或者,也许我应该停止计时器,重新启动秒表,然后再次启动计时器,就像这样?:
Timer.Stop
Stopwatch.Restart
Timer.Start
我对如何继续感到不安是因为我完全不了解 .NET 同步对象的实际工作原理。我尝试过在不同的地方添加一些 SyncLock,但我真的不知道它们是否正确实现。我想知道,在提供了所有这些背景信息后,是否有真正聪明的人能够告诉我我有多愚蠢以及如何正确地做到这一点。我真的很感激任何意见。如果提供一些代码片段有用,我会很高兴,我只是担心一切都如此复杂,以至于它只会分散我所希望的概念性问题的注意力。
提前致谢! 布莱恩
VB.NET, .NET 4
Hello all,
I have an application that controls an industrial system. It has a GUI which, once a process is started, principally displays the states of various attached devices. It basically works like this:
- A System.Timers.Timer object is always running. At each Elapsed event, it polls the devices for their current values and invokes controls on the GUI, updating them with the new values.
- A start button is clicked, a process time Stopwatch object is created and started (Labels on the GUI are now invoked and updated on the System.Timers.Timer's Elapsed event, in addition to the other work that is taken care of on this event)
- A new thread is created which runs a Process() subroutine
- Some Stopwatch objects are created and started (these Stopwatches are periodically restarted during the process via their Restart() method.
- Some logic is executed on the new Stopwatchs' Elapsedmilliseconds properties to determine when to do things like write new setpoints to the devices, update the data log, etc...
Here's my problem: The program occasionally freezes. My ignorant efforts at tracking down the problem have led me to suspect that read/writes to the subset of devices that are RS-232 controlled are the culprits most of the time. However, I occasionally see other strange things upon program freeze, e.g., one of the time Labels whose Text property is determined by a Stopwatch's Elapsedmilliseconds property sometimes will show an impossible value (e.g., -50 hours or something).
For the RS-232 problems, I suspect something like a read event is being executed at the same time as a write event and this causes a freeze(?). I tried to prevent this by making sure that all communication with an RS-232 device is funneled through a Transmit() subroutine which has the following attribute:
Which, as far as my ignorance permits me to understand, should force one Transmit() execution to finish completely before another one can start. Perhaps another risk is the code getting blocked here if one Transmit() never finishes?
Regarding the Stopwatch trouble, I speculate that the problem is that the Timer is trying to update a GUI Label at the same time that the Stopwatch's Restart() method is being executed. I'm unsure if this could cause a problem. All I know is that this problem has only occurred at a point in the process when a Restart() call would be made.
I am wondering if I could use a SyncLock or something to lock a Stopwatch while the Label is being updated (or, conversely, while its being restarted)? Or, perhaps I should stop the Timer, restart the Stopwatch, and then start the timer again, like so?:
Timer.Stop
Stopwatch.Restart
Timer.Start
My trepidation regarding how to proceed is due to my complete lack of understanding of how .NET synchronization objects actually work. I've tried slapping a few SyncLocks in various places, but I really have no idea if they're implemented correctly or not. I'm wondering if, having provided all this context, someone really smart might be able to tell me how I'm stupid and how to do this right. I would really appreciate any input. If it would be useful to provide some code snippets, I'd be happy to, I just worry that everything's so convoluted that it would just detract from what I'm hoping is a conceptual question.
Thanks in advance!
Brian
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
如果您从事与 SCADA 相关的任何工作,我会考虑转向任务调度框架,而不是依赖于手动操作计时器。一个简单的起点类似于 hardcodet.Scheduling 类,您可以转向 Quartz 之类的东西。大多数此类框架将为您提供暂停和恢复计划操作的方法。
如果我使用 Modbus,我通常会保留寄存器值的本地缓存,并对任何值进行更改都会触发更改事件。这样做的好处是允许您实现手动刷新值等操作,而不会干扰进程调度以及在评估轮询响应时检查死区。这恰好是对 OPC DA 接口的子集实施轮询协议的副作用。
I would consider a shift to a task scheduling framework instead of relying on manual manipulation of timers if your working on anything SCADA related. A simple starting point would be something similar to the hardcodet.Scheduling classes and you can move to something like the beast that is Quartz. Most of these types of frameworks will provide you with a way to pause and resume scheduled actions.
If I'm working with Modbus, I normally keep a local cache of the register values and make changes to any value fire a change event. This has the benefit of allowing you to implement things like refreshing values manually without interfering with your process scheduling and checking for deadband when evaluating your polled response. This happened to be the side effect of implementing a polled protocol to a subset of the OPC DA interface.