需要 C# 线程帮助

标签 c# multithreading

我被要求编写一种方法,允许调用者通过串行端口向硬件设备发送命令字符串。发送命令后,该方法必须等待设备的响应,然后将响应返回给调用者。

让事情变得复杂的是,硬件设备定期向 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/

相关文章:

ios - 替换多线程核心数据中的数据

c++ - MFC 应用程序在 CSingleLock Lock() 后挂起

c# - 使用普通滚动动画滚动到 Scrollviewer 中的某个位置

c# - 如何阻止 UpdatePanel 导致整个页面回发?

c# - 将 2 个单 float 合二为一

multithreading - 跨线程合并更改时如何防止竞争状况?

c - 进程内部/外部生成的不同内存转储

python - 创建中断 (ISR) 以创建平滑的机械臂运动

c# - 为什么我在 Windows 10 UWP 上收到 "package could not be registered"部署错误?

c# - 未触发 DropDownList 的 SelectedIndexChanged 事件