首页 文章

需要C#线程帮助

提问于
浏览
2

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

使事情复杂化的是,硬件设备定期向PC发送未经请求的数据包(应用程序必须存储的数据用于报告) . 因此,当我发送串行命令时,我可能会在收到命令响应之前收到一个或多个数据包 .

其他注意事项:可能有多个客户端可能同时发送串行命令,因为此方法将构成WCF服务的基础 . 此外,该方法需要是同步的(因为我不会在这里讨论),因此规则使用回调将响应返回给客户端 .

关于“多个客户端”,我计划使用BlockingCollection <>对传入的命令进行排队,后台线程一次执行一个任务,从而避免串口争用 .

但是我不知道如何处理传入的串行数据 . 我最初的想法是有另一个后台线程,它不断读取串口,存储数据分析包,还要查找命令响应 . 当收到一个线程时,线程会以某种方式将响应数据返回给最初发送串行命令的方法(自从这样做以来一直在等待 - 请记住我有一个规则,即该方法是同步的) .

这是我不确定的最后一点 - 如何让我的方法等到后台线程收到命令的响应?我怎样才能将后台线程的响应传递给我的等待方法,以便将它返回给调用者?我是线程新手,所以我采取了错误的方式吗?

提前致谢

安迪

4 回答

  • 2

    首先:当您使用框架附带的 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 属性的单例类来访问单例实例 . 这个类完成所有串口的工作 . 它还应该包含一个在"out of band"信息进入时引发的事件(不是对命令的回复的数据) .

    它还包含 SendRequest 方法,该方法将命令发送到串行设备,将状态对象存储在内部列表中,等待命令的回复进入并使用回复更新状态对象 .

    状态对象包含一个名为 ReplyReceived 的等待句柄,它在更改状态对象的 Reply 属性后由 SerialPortHandler 设置 . 这样你就不需要循环和 Thread.Sleep . 此外,不是调用 WaitOne() 而是调用 WaitOne(timeout)timeout 是等待回复进来的毫秒数 . 这样你就可以实现某种超时功能 .

    这是它在 SerialPortHandler 中的样子:

    void HandlePossibleCommandReply(string reply)
    {
        StateObject state = FindStateObjectForReply(reply);
        if (state != null)
        {
            state.Reply = reply;
            state.ReplyReceived.Set();
    
            m_internalStateList.Remove(state);
        }
    }
    

    请注意:这就是我'd try to start with. I'我确定这可以非常优化,但正如你所看到的那样"multithreading"参与的地方不多 - 只应调用 SendAndWait 方法,以便多个客户端可以发出命令,而另一个客户端仍然等待它的回应 .

    EDIT
    另一个注意事项:您说该方法应该构成WCF服务的基础 . 这使事情变得更容易,就像您配置服务权限一样,将为每次调用服务创建服务类的实例,因此 SendAndWait 方法将在其自己的服务实例中"live",甚至不需要重新进入 . 在这种情况下,您只需要确保 SerialPortHandler 始终处于活动状态(=>创建并独立于实际的WCF服务运行),无论是否目前有一个服务类的实例 .

    EDIT 2
    我将示例代码更改为不按照注释中的建议循环和休眠 .

  • -1

    如果你真的想要阻塞,直到后台线程收到你的命令响应,你可以考虑让后台线程在你排队命令并将其返回给你时锁定一个对象 . 接下来,您等待锁定并继续:

    // in main code:
    var locker = mySerialManager.Enquee(command);
    lock (locker)
    {
         // this will only be executed, when mySerialManager unlocks the lock
    }
    
    // in SerialManager
    public object Enqueue(object command)
    {
        var locker = new Object();
        Monitor.Enter(locker); 
        // NOTE: Monitor.Exit() gets called when command result 
        // arrives on serial port
        EnqueueCommand(command, locker);
        return locker;
    }
    
  • 1

    几件事 . 您需要能够将串行响应绑定到请求它们的命令 . 我假设有一些索引或序列号与命令一起出现并返回到响应中?

    鉴于此,你应该没问题 . 您需要某种“serialAPU”类来表示请求和响应 . 我不知道这些是什么,也许只是字符串,我不知道 . 该类也应该有一个autoResetEvent . 无论如何,在你的'DoSerialProtocol()'函数中,创建一个serialAPU,用请求数据加载它,将它排队到串行线程并等待autoResetEvent . 当线程获得serialAPU时,它可以在serialAPU中存储索引/序列号,将serialAPU存储在向量中并发送请求 .

    当数据进入时,你是协议的东西,如果数据是有效的响应,从数据中获取索引/序列,并在向量中的serialAPU中查找匹配值 . 从向量中删除匹配的serialAPU,使用响应数据加载它并发出autoResetEvent信号 . 最初调用'DoSerialProtocol()'的线程将运行并可以处理响应数据 .

    当然有很多'摆动' . 超时是一个 . 我很想在serialAPU中有一个状态枚举,受到CritcalSection或atomicCompareandSwap的保护,初始化为'Esubmitted' . 如果oringinating线程超时等待autoResetEvent,它会尝试将其serialAPU中的状态枚举设置为'EtimedOut' . 如果成功,很好,它会向调用者返回一个错误 . 同样地,在串行线程中,如果它找到状态为EtimedOut的serialAPU,它只是将其从容器中删除 . 如果它找到与响应数据匹配的serialAPU,它会尝试将状态更改为“EdataRx”并且如果成功 . 触发autoRestEvent .

    另一个是讨厌的OOB数据 . 如果有,请创建一个serialAPU,加载OOB数据,将状态设置为'EOOBdata'并用它调用一些'OOBevent' .

  • 1

    我建议你看一下BackgroundWorker -Class

    Ther是此类中的一个事件(RunWorkerCompleted),当工作人员完成工作时会触发该事件 .

相关问题