我有C#多线程应用程序,必须使用SerialPort与硬件连接。
该程序主要是命令响应序列,但由于内部错误,硬件可以发送未经请求的“RESET”消息,此时软件必须通过发送一系列设置某些值的命令重新初始化它。
多个线程(来自线程池)可以尝试执行TakeSampleNow()
public class ALComm
{
private readonly AutoLoaderManager _manager;
private readonly AutoResetEvent dataArrived = new AutoResetEvent(false);
private SerialPort _alPort;
private string _alResponse;
.... Code to init _alPort and attach datareceived event etc
public void TakeSampleNow()
{
if (Monitor.TryEnter(_alPort, 1000)) //let's wait a second
{
_manager.MessageList.Enqueue("Try sampling");
try
{
Send("Command1");
string response = Receive();
switch(response)
{
case "X": blah blah..
case "Y": blah blah..
}
Send("Command2");
string response = Receive();
while(response != "OK")
{
Send("Command3");
string response = Receive();
Send("Command2");
string response = Receive();
}
}
finally
{
Console.WriteLine("Releasing port");
//Thread.CurrentThread.Priority = ThreadPriority.Normal;
Monitor.Exit(_alPort);
}
else
{
_manager.MessageList.Enqueue("Port is busy!!!");
}
}
public string Receive()
{
string inString = null;
dataArrived.WaitOne(1000);
inString = _alResponse;
return inString;
}
private void AlPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
_alResponse = _alPort.ReadLine();
//if (_alResponse.ToUpper().Contains("RESET"))
//{
// _alState = AlState.Reset;
// TryInitialize();
//}
dataArrived.Set();
}
private void TryInitialize()
{
Monitor.Enter(_alPort); //lock so other threads do not access samplenow during initialization
try
{
string response;
Console.WriteLine("Initializing ... The AutoLoader");
_alPort.DiscardInBuffer();
Send("CommandX");
response = Receive();
--- blah blah
_alResponse = "";
}
finally
{
Monitor.Exit(_alPort);
}
}
我可以检查datareceived事件中的响应并等待TryInitialize()中的锁定以及TakeSampleNow中的其他线程释放锁定,如果_alResponse包含“RESET”并且它确实返回,我将不得不检查每个响应从方法。这让它更复杂。
有关如何更好地做到这一点的任何建议。我相信它可以是状态机,但我无法将其概念化。
答案 0 :(得分:1)
您不希望多个线程尝试读取您的串行端口。您应该有一个单独的线程,除了读取端口之外什么也不做。当它获取一些数据时,它会将消息放入队列或类似的数据结构中,这些数据结构可由多个样本线程处理。这样,您的单个读取器线程就可以可靠地查找并响应RESET消息。
答案 1 :(得分:1)
您没有提供协议的详细信息 - 您没有说明命令/ rsponse对是否可以重叠,如果是,则说明响应如何与命令匹配。
您应该可以使用状态引擎执行此操作。使用自己的线程运行状态机,该线程在BlockingCollection上等待事件。您还需要一个'SerialRecv'线程来运行您的协议并将传入的字节解析为消息。
我只使用一个'SerialEvent'类将事件传递到SM队列中。该类应该有一个枚举描述RX缓冲区中的事件和成员,TX数据,分析数据,数据可以集合,异常/ errorMess场TX字符串 - 需要任何事件或转发的目的,(如一切SM可能。将完成的请求/响应转发给显示器或记录器。)
我能直接想到的一些事件:EsmNewRequestResponse,EsmRxData,EsmResetRx
事件枚举可以作为某些阶段具有SM未使用的其他值,例如:EsmError,EsmLog,EsmDisplay。
如果需要超时,可以通过超时SM输入队列中的take()来生成一个。
是的,我遗漏了一些东西。
如果多个线程同时发出SerialEvent实例,那么SM将在处理第一个串行事件时获得新的SerialEvent。 SM将需要另一个队列/双端队列来保存等待处理的SerialEvents。由于BlockingCollection / thread对SM进行序列化,因此该“挂起”队列不必是线程安全的。在任何请求/响应完成后,SM应检查此待处理队列,以查看是否还有另一个要处理的队列。
为了从多个线程同步处理请求/响应,请求线程必须具有等待的东西。 SerialEvent类中的AutoResetEvent可以。向系统提交SerialEvent将排队SerialEvent实例并等待AutoResetEvent。当实例的处理完成时(即收到响应,错误或超时),SM将设置事件,并且原始线程将在其SerialEvent实例填入数据的情况下运行。
接下来 - SerialEvent类正朝着可能更好地集中它们而不是不断创建/ CG的方向发展。这需要另一个BlockingCollection作为池。