【发布时间】:2015-06-23 14:03:27
【问题描述】:
我目前有一个 .NET 应用程序,其中一个函数(在一个表单中)调用一个公共函数(位于一个模块中)。表单函数 (WriteValueToTag) 将一些值传递给模块函数 (WriteToPLC),它返回 True of False。模块函数 (WriteToPLC) 始终从 WriteValueToTag 调用(从不直接调用)并且仅在表单内使用。但是,表单上有一个第 3 方控件,它引发了一个事件,它调用 WriteValueToTag 函数。因此,它在与主 UI 窗体不同的线程上调用。
我遇到的问题是(根据我的日志记录)似乎有时会从不同的线程同时调用此函数。并且参数值出现不同步导致错误。这两个函数都不会更新表单上的任何数据或控件(因此我不检查 Invoke.Required 等)我不确定是否应该在我的函数中进行此检查或在其自己的线程中启动 WriteToPLC 函数使用 Thread.Start)?我尝试在 WriteToPLC 模块函数的全部内容周围放置一个 SyncLock,但这似乎没有帮助。
如何处理模块中公共函数的调用,该函数接收参数,可以随时从不同的线程调用,以便每个调用它的线程唯一地运行?任何示例或链接都会有所帮助。谢谢。
模块中的函数声明如下:
Public Function WriteToPLC(ByRef PLC As Logix.Controller, ByRef PLCTag As Logix.Tag) As Boolean
调用 this 的子程序声明如下:
Private Function WriteValueToTag(ByVal PLCTagValue As Object, ByRef PLCTagName As Logix.Tag) As Boolean
...这里WriteToPLC被称为传入适当的值。
我想确保在使用 Invoke 时保留 ByRef 参数,所以我不确定我的 WriteValueToTag 函数的以下代码是否正确:
Delegate Function DelegateWriteValueToTag(ByVal PLCTagValue As Object, ByRef PLCTagName As Logix.Tag)
Private Function WriteValueToTag(ByVal PLCTagValue As Object, ByRef PLCTagName As Logix.Tag) As Boolean
''Dim writeLock As New Object
Dim tagName As Logix.Tag = PLCTagName 'remember PLCTagName since it's a ByRef param
If (InvokeRequired) Then
Dim newDelegate As New DelegateWriteValueToTag(AddressOf WriteValueToTag)
Dim eventArgs() As Object = {PLCTagValue, tagName}
Log.Debug("Calling Function WriteValueToTag() via InvokeRequired")
Dim result As Boolean
result = CType(Invoke(newDelegate, eventArgs), Boolean)
PLCTagName.Value = tagName.Value 'put value back in since PLCTagName param is passed ByRef
Return result
Else
Try
Log.Debug("Entering Function WriteValueToTag()")
Log.Debug(" writing value {0} to tag {1}", PLCTagValue, tagName.Name)
PLCTagName.Value = PLCTagValue
Return(WriteToPLC(mPLC, PLCTagName))
Catch ex As Exception
HandleError(ex, False)
Return False
End Try
End If
End Function
这是调用 WriteValueToTag 以保留 ByRef 参数的正确方法吗?我想知道,因为我使用“eventArgs()”来传递作为对象的变量,这是否会传递对该对象的引用(即使 Invoke 的参数被声明为 ByVal)。如果是这样,我是否还需要声明任何局部变量 tagName 来存储这个值,然后在 Invoke 之后将其设置回来?
也想知道会不会像这样简单:
If (InvokeRequired) Then
Dim newDelegate As New DelegateWriteValueToTag(AddressOf WriteValueToTag)
Dim eventArgs() As Object = {PLCTagValue, PLCTagName}
Log.Debug("Calling Function WriteValueToTag() via InvokeRequired")
Dim result As Boolean
result = CType(Invoke(newDelegate, eventArgs), Boolean)
PLCTagName.Value = PLCTagValue 'retain PLCTagName.Value as it's passed ByRef and Invoke will not change it
Return result
...
【问题讨论】:
-
我感觉问题不在于“参数混淆”(每个线程都有自己的调用堆栈来跟踪传递给方法的参数)。问题可能出在 WriteToPLC 本身 - 无论它在做什么,一次应该只有一个线程在做。
-
@mbeckish - 同意。那么,我怎样才能确保 WriteToPLC 只为每个调用它的线程做它应该做的事情呢?
-
由于您没有展示
WriteToPLC所做的事情或具体解释它做错了什么,因此无法说... -
万一,你在哪里打电话给
Application.DoEvents()?或者您是否在WriteValueToTag内做任何会直接或间接导致第 3 方控制触发的事情?是什么让您认为涉及的线程不止一个?您可以通过设置断点并在 Visual Studio 中查看线程列表来验证吗? -
我会实现Producer / Consumer pattern。调用“WriteToPLC”的线程应该将请求对象放入队列中。只有 1 个消费者线程处理请求并对
WriteToPLC进行实际调用,一次一个。
标签: c# .net vb.net multithreading