首页 > 解决方案 > 设备控制线程数据接收事件错误

问题描述

我和我的同事花了几天时间寻找解决方案,大多数回复都说要摆脱不必要的线程。这是情况。

我最多控制 1-55 台设备(取决于用户需要)。我必须几乎同时初始化每个设备。我通过Task为每个调用进行一次调用来完成此操作,该调用InitializeDevice(deviceNumber)将启动每个设备的初始化,然后轮询设备直到完成。

SerialPort.Write()覆盖功能被编辑,lock因此一次只能写入一个设备,所以这不是我的问题(经过彻底测试)。当我为 3 台设备创建任务时,我很好。当我为 4 个设备创建任务时,我的串行端口停止正常工作(尽管它适用于我的同事,他们有 6 个核心,而我的 4 个核心)。该DataReceived事件会在短时间内停止触发,如下所述。

Write(command1); // Attempt 1, task 1 fails to get a response when responseTimeout.WaitOne(50) times out
Write(command1); // Attempt 2, task 1 fails to get a response due to 50ms timeout
Write(command1); // Attempt 3, task 1 fails to get a response due to 50ms timeout
Write(command1); // Attempt 4, task 1 fails to get a response due to 50ms timeout
Write(command1); // Attempt 5, task 1 fails to get a response due to 50ms timeout
DataReceived event is triggered and at this point BytesToWrite contains all the data that has been accumulated.
After this initial DataReceived event delay, everything proceeds as expected. (this is one of the biggest reasons that I believe that it is either the creation or starting of the `Task`s that breaks the port)

我们无法摆脱这些任务,因为对于我们的应用程序而言,看似同时启动很重要。

为了测试创建Tasks 是否是瓶颈,我们尝试创建所有Tasks 然后同时启动它们,但这也不起作用,这使得启动线程似乎是问题所在。为了测试是否启动Tasks 是问题,我们设置了一个断点,它被我们在调用函数之前所做的所有 4 s命中InitializeDevice(deviceNumber)。由于所有 4 都已创建、启动并在任何s 生成之前运行,因此您会认为s 不会成为瓶颈。当我将from撞到s 时都成功了,尽管 the和 when之间的延迟TaskSerialPort.Write()WriteTaskresponseTimeout.WaitOne(time)501000WriteWriteDataReceived触发时间分别为:990ms、6ms、15ms、15ms、16ms等。平均响应时间为15ms左右。我们连接了一个使用和不Task使用 s 的逻辑分析仪,响应时间总是大约 5 毫秒,所以我们并没有失去响应,只是事件没有在合理的时间内触发。我们尝试设置DataReceived为最高优先级并将初始化设置Task为最低无济于事。

一切都表明这是一个问题,Task即 s 或线程创建会占用处理器速度并防止DataReceived在合理的时间内触发。我们不能无限期地等待响应,我们必须同时启动所有设备。在这一点上,我们已经用完了解决方案。


示例代码

public class DeviceControl
{
    private static void Initialize_AllDevices(CustomSerialPort port, List<Device> devices)
    {
        for (int i = 0; i < devices.Count; i++)
        {
            DeviceNumber deviceNumber = i;

            Task.Factory.StartNew(
                () => Initialize_SingleDevice(port, devices, deviceNumber));
        }
    }

    private static void Initialize_SingleDevice(CustomSerialPort port, List<Device> devices, int deviceNumber)
    {
        List<byte> command = devices[deviceNumber].CreateCommand();
        port.Write(command);
    }
}

public class CustomSerialPort : SerialPort
{
    private AutoResetEvent _responseTimeout;

    public CustomSerialPort()
    {
        DataReceived += Read;
        _responseTimeout = new AutoResetEvent(false);
    }

    private void Read(object sender, SerialDataReceivedEventArgs eventArgs)
    {
        // This is what is not reached until Task creation is complete.
        // Parse response
        if (ReadSuccessful())
        {
            _responseTimeout.Set();
        }
    }

    public void Write()
    {
        lock (new object())
        {
            Write(command.ToArray(), 0, command.Count);

            // Wait until response timeout is hit or response is received.
            _responseTimeout.Reset();
            if (!_responseTimeout.WaitOne(50))
            {
                // uh oh
            }
        }
    }
}

标签: c#multithreadingeventsserial-porttask

解决方案


推荐阅读