我被要求编写一种方法,允许调用者通过串行端口向硬件设备发送命令字符串。发送命令后,该方法必须等待设备的响应,然后将响应返回给调用者。
让事情变得复杂的是,硬件设备定期向 PC 发送未经请求的数据包(应用程序必须存储以进行报告的数据)。因此,当我发送串口命令时,在收到命令响应之前,我可能会收到一个或多个数据包。
其他注意事项:可能有多个客户端可能同时发送串行命令,因为此方法将构成 WCF 服务的基础。另外,该方法需要是同步的(由于我不会在这里讨论的原因),因此排除了使用回调将响应返回给客户端的可能性。
关于“多个客户端”,我计划使用 BlockingCollection<> 对传入命令进行排队,并使用一个后台线程一次执行一个任务,从而避免串行端口争用。
但是我不确定如何处理传入的串行数据。我最初的想法是有另一个后台线程不断读取串行端口,存储数据分析数据包,但也寻找命令响应。当收到一个消息时,线程会以某种方式将响应数据返回到最初发送串行命令的方法(从那时起它就一直在等待 - 请记住我有一个规定,该方法是同步的)。
这是我不确定的最后一点 - 如何让我的方法等待后台线程收到命令的响应?如何将后台线程的响应传递给我的等待方法,以便它可以将其返回给调用者?我是线程新手,所以我的处理方式是否错误?
提前致谢
安迪
最佳答案
首先:当您使用框架自带的SerialPort
类时,数据接收事件已经是异步的。当您发送内容时,数据会异步传入。
我尝试的是:将所有需要等待答案的请求排队。在整个接收处理程序中,检查传入数据是否是某个请求的答案。如果是这样,则将回复与请求信息一起存储(为此创建某种状态类)。所有其他传入数据均正常处理。
那么,如何让请求等待答复呢?发送命令并返回答复的调用将创建状态对象,将其排队并监视该对象以查看是否收到答复。如果收到应答,调用将返回结果。
一个可能的大纲可能是:
string SendAndWait(string command)
{
StateObject state = new StateObject(command);
state.ReplyReceived = new ManualResetEvent(false);
try
{
SerialPortHandler.Instance.SendRequest(command, state);
state.ReplyReceived.WaitOne();
}
finally
{
state.ReplyReceived.Close();
}
return state.Reply;
}
什么是SerialPortHandler
?我将其设为一个单例类,其中包含一个用于访问单例实例的 Instance 属性。这个类完成所有串行端口的工作。它还应该包含一个在“带外”信息进入时引发的事件(不是对命令的回复的数据)。
它还包含 SendRequest
方法,该方法将命令发送到串行设备,将状态对象存储在内部列表中,等待命令的答复传入并使用答复更新状态对象.
状态对象包含一个名为 ReplyReceived
的等待句柄,该句柄由 SerialPortHandler
在更改状态对象的 Reply
属性后设置。这样你就不需要循环和Thread.Sleep。另外,您可以调用 WaitOne(timeout)
,而不是调用 WaitOne()
,其中 timeout
是等待回复的毫秒数这样你就可以实现某种超时功能。
这就是它在 SerialPortHandler
中的样子:
void HandlePossibleCommandReply(string reply)
{
StateObject state = FindStateObjectForReply(reply);
if (state != null)
{
state.Reply = reply;
state.ReplyReceived.Set();
m_internalStateList.Remove(state);
}
}
请注意:这是我尝试开始的。我确信这可以得到很大的优化,但是正如您所看到的,其中没有涉及太多“多线程” - 只有 SendAndWait
方法应该以某种方式调用,以便多个客户端可以发出命令,而另一个客户端可以发出命令客户端仍在等待其响应。
编辑
另请注意:您说该方法应该构成 WCF 服务的基础。这使事情变得更容易,就像您正确配置服务一样,将为每次调用服务创建服务类的实例,因此 SendAndWait
方法将“存在”在它自己的实例中服务,甚至根本不需要重新进入。在这种情况下,您只需确保 SerialPortHandler
始终处于事件状态(=> 独立于实际的 WCF 服务创建和运行),无论当前是否有您的服务类的实例全部。
编辑2
我按照评论中的建议将示例代码更改为不循环和 sleep 。
关于需要 C# 线程帮助,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10208469/