C# 线程-多个SerialPort和一个DataGridView
我有3个C# 线程-多个SerialPort和一个DataGridView,c#,multithreading,event-handling,serial-port,queue,C#,Multithreading,Event Handling,Serial Port,Queue,我有3个SerialPort组件,其中2个有DataReceived处理程序。 当接收到数据时,它们通过其他SerialPort (第三个)。 我知道,在我的应用程序中,两个串行端口同时尝试发送的可能性不高,但我尝试模拟这种情况,当然,我得到一个错误,即正在使用COM端口 private void sendToCOM(String comNumber, String msg, String speed) { try { using
SerialPort
组件,其中2个有DataReceived
处理程序。
当接收到数据时,它们通过其他SerialPort
(第三个)。
我知道,在我的应用程序中,两个串行端口
同时尝试发送的可能性不高,但我尝试模拟这种情况,当然,我得到一个错误,即正在使用COM端口
private void sendToCOM(String comNumber, String msg, String speed)
{
try
{
using (SerialPort comPort = new SerialPort(comNumber, Int32.Parse(speed), Parity.None, 8, StopBits.One))
{
comPort.Open();
comPort.Write(msg);
comPort.Close();
comPort.Dispose();
}
}
catch(Exception ex)
{
cstFuncs.errorHandler(ex.Message, SettingsForm);
}
}
我知道每个SerialPort
都有自己的线程
如何创建队列或如何防止两个串行端口同时尝试访问相同的资源(并且不丢失应该发送的数据)您可以做一些事情 1) 建立一个锁机制。在尝试访问该方法之前,线程将等待该锁被解锁,这意味着每个线程将轮流触发“sendToCOM” 2) 另一种方法是使用ConcurrentQueue(请参阅:)。这是一个线程安全的列表,如果需要,您也可以添加COMport对象,然后从那里启动它们。例如,关于这样做有几个问题
我相信还有更多的方法,但这是我脑子里想出来的两种方法。我认为第二种方法更可靠,但需要更多的工作才能开始运行,因此第一种方法可能是你最感兴趣的 1) 建立一个锁机制。在尝试访问该方法之前,线程将等待该锁被解锁,这意味着每个线程将轮流触发“sendToCOM” 2) 另一种方法是使用ConcurrentQueue(请参阅:)。这是一个线程安全的列表,如果需要,您也可以添加COMport对象,然后从那里启动它们。例如,关于这样做有几个问题
我相信还有更多的方法,但这是我脑子里想出来的两种方法。我想说第二个更可靠,但需要更多的工作才能运行,因此第一个可能是您最感兴趣的。您可能没有每个串行端口都有一个线程,而是在线程池中执行
DataReceived
处理程序。这仍然意味着你可以一次读两遍
您可以使用锁定来解决此问题:
将OutputPortLock对象添加到类:
private object OutputPortLock = new object();
并在写入输出端口之前尝试锁定它:
lock(OutputPortLock)
{
using(SerialPort comPort=....)
{
... write to com port ...
}
}
这将确保一次只有一个线程尝试写入输出端口。如果其他进程尝试使用Com端口,您可能仍然会收到Com端口使用错误。您可能没有每个串行端口的线程,而是在线程池中执行
DataReceived
处理程序。这仍然意味着你可以一次读两遍
您可以使用锁定来解决此问题:
将OutputPortLock对象添加到类:
private object OutputPortLock = new object();
并在写入输出端口之前尝试锁定它:
lock(OutputPortLock)
{
using(SerialPort comPort=....)
{
... write to com port ...
}
}
这将确保一次只有一个线程尝试写入输出端口。如果其他进程尝试使用Com端口,您可能仍然会收到Com端口使用错误。有几种方法可以在
sendToCOM
方法中获得锁定。这将导致任何后续调用等待第一个调用完成,从而停止该方法执行两次。如果另一个进程尝试使用相同的SerialPort
,这将没有帮助,但它将停止任何本地争用
一种方法是在如下对象上使用lock
关键字:
private readonly object _myLock = new object();
private void sendToCOM(String comNumber, String msg, String speed)
{
try
{
lock(_myLock)
{
using (SerialPort comPort = new SerialPort(comNumber, Int32.Parse(speed), Parity.None, 8, StopBits.One))
{
comPort.Open();
comPort.Write(msg);
comPort.Close();
comPort.Dispose();
}
}
}
catch(Exception ex)
{
cstFuncs.errorHandler(ex.Message, SettingsForm);
}
}
另一种方法是使用ManualResetEventSlim
(或类似工具)来实现相同的效果:
private ManualResetEventSlim _myLock = new ManualResetEventSlim(true);
private void sendToCOM(String comNumber, String msg, String speed)
{
_myLock.Wait();
try
{
_myLock.Reset();
using (SerialPort comPort = new SerialPort(comNumber, Int32.Parse(speed), Parity.None, 8, StopBits.One))
{
// ..
}
}
catch(Exception ex)
{
cstFuncs.errorHandler(ex.Message, SettingsForm);
}
finally
{
_myLock.Set();
}
}
有几种方法可以锁定
sendToCOM
方法。这将导致任何后续调用等待第一个调用完成,从而停止该方法执行两次。如果另一个进程尝试使用相同的SerialPort
,这将没有帮助,但它将停止任何本地争用
一种方法是在如下对象上使用lock
关键字:
private readonly object _myLock = new object();
private void sendToCOM(String comNumber, String msg, String speed)
{
try
{
lock(_myLock)
{
using (SerialPort comPort = new SerialPort(comNumber, Int32.Parse(speed), Parity.None, 8, StopBits.One))
{
comPort.Open();
comPort.Write(msg);
comPort.Close();
comPort.Dispose();
}
}
}
catch(Exception ex)
{
cstFuncs.errorHandler(ex.Message, SettingsForm);
}
}
另一种方法是使用ManualResetEventSlim
(或类似工具)来实现相同的效果:
private ManualResetEventSlim _myLock = new ManualResetEventSlim(true);
private void sendToCOM(String comNumber, String msg, String speed)
{
_myLock.Wait();
try
{
_myLock.Reset();
using (SerialPort comPort = new SerialPort(comNumber, Int32.Parse(speed), Parity.None, 8, StopBits.One))
{
// ..
}
}
catch(Exception ex)
{
cstFuncs.errorHandler(ex.Message, SettingsForm);
}
finally
{
_myLock.Set();
}
}
非常感谢你的帮助。看起来是这样的。。我会读更多关于它的内容。非常感谢你的帮助。看起来是这样的。。我会读更多关于它的内容。谢谢你的帮助。这两种方法有什么区别?我尝试了第二种方法,它看起来很有效,但我成功地让它崩溃了一次(我有两个应用程序,每100ms向每个com端口发送一个字符串)。区别在于实际使用情况。我建议使用
lock
老实说,ManualResetEventSlim
只是我一直在玩弄的一个想法,应该仍然有效。您确定崩溃是由此方法引起的吗?应用程序没有崩溃(“catch”捕捉到它),异常是对端口“COM6”的访问被拒绝。
。在我尝试植入这种锁定(第二种方法)之前,我得到了很多。现在我得到的错误少了很多,但我仍然得到一些。。我现在要试试第一种方法。那确实很奇怪。老实说,我根本没有使用过SerialPorts,因此在这方面我无法给出更可靠的建议。然而,我知道这是不好的,但在开始时或在最后
中人为引起的延迟可能会有所帮助?(谈论Thread.Sleep
)我现在尝试了第一种方法,它减慢了整个过程(但我现在没有收到错误)也许正如您所建议的,稍微延迟一下,然后一起使用第二种方法可以解决问题。事实上,我并不需要这么高的速度,但是如果有一天需要的话,我会尽我最大的努力,谢谢你的帮助。这两种方法有什么区别?我尝试了第二种方法,它看起来很有效,但我成功地让它崩溃了一次(我有两个应用程序)