You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Device.BeginInvokeOnMainThread顺序执行?TCP数据UI绑定问题

解决TCP数据顺序添加至DataGrid的问题

问题根源

你当前的代码存在两个核心问题:

  1. 每次接收数据就启动Task.Run并行处理,导致共享变量m_nCurRow可能被多线程并发读写,直接破坏数据的顺序和正确性。
  2. 短时间内大量Device.BeginInvokeOnMainThread回调被塞入UI消息队列,UI线程会批量处理这些任务,视觉上表现为所有数据一次性加载,而非逐条顺序添加。

解决方案

下面提供两种可靠的实现方式,确保数据按接收顺序逐条更新UI:


方案一:用信号量串行处理数据

通过SemaphoreSlim强制每条数据的处理逻辑(模型创建+UI更新)串行执行,彻底避免并发问题,同时保证UI逐条更新。

首先定义一个全局信号量:

private readonly SemaphoreSlim _processingLock = new SemaphoreSlim(1, 1);

然后修改数据接收后的处理代码:

// 等待前一条数据处理完成
await _processingLock.WaitAsync();
try
{
    // 直接在当前线程创建数据模型(此逻辑耗时极低,无需额外开Task)
    var data = new QuantitationMeasureData()
    {
        GroupNumber = m_nCurRow,
        GroupName = $"Group{m_nCurRow}",
        SampleName = $"Sample{m_nCurRow}",
        Cell = e.Cell.ToString(),
        Time = GlobalParam.Time.Now,
        Wave = m_Model.STCInfo.Wavelength,
        Base1 = m_Model.STCInfo.BaseWave1,
        Base2 = m_Model.STCInfo.BaseWave2,
        Trans = e.Trans,
        ABS = dRetABS,
        Conc = dRetConc,
        Factor = m_Model.Factor,
        AVG = dRetABS,
        STD = dRetConc,
    };

    // 使用InvokeOnMainThreadAsync等待UI操作完成,再处理下一条
    await Device.InvokeOnMainThreadAsync(() =>
    {
        m_Model.MeasureDataCollection.Add(data);
        // 如果MeasureDataCollection是ObservableCollection,可去掉RefreshData,UI会自动更新
        // xDataGrid.RefreshData();
    });

    // 确保当前行序号在UI更新后再递增,避免并发问题
    m_nCurRow++;
}
finally
{
    // 释放信号量,允许处理下一条数据
    _processingLock.Release();
}

方案二:用Channel实现生产者-消费者模式

通过Channel将数据接收(生产者)和UI更新(消费者)解耦,保证数据按接收顺序进入队列,消费者单线程逐条处理,彻底杜绝并发问题。

首先定义全局Channel(需要包含所有创建模型所需的参数):

// 替换YourEventArgs为实际的TCP接收事件类型
private readonly Channel<(YourEventArgs e, double dRetABS, double dRetConc)> _dataChannel = 
    Channel.CreateUnbounded<(YourEventArgs e, double dRetABS, double dRetConc)>();

在程序初始化时启动消费者任务:

_ = Task.Run(async () =>
{
    // 按顺序读取队列中的数据
    await foreach (var item in _dataChannel.Reader.ReadAllAsync())
    {
        var (e, dRetABS, dRetConc) = item;
        var data = new QuantitationMeasureData()
        {
            GroupNumber = m_nCurRow,
            GroupName = $"Group{m_nCurRow}",
            SampleName = $"Sample{m_nCurRow}",
            Cell = e.Cell.ToString(),
            Time = GlobalParam.Time.Now,
            Wave = m_Model.STCInfo.Wavelength,
            Base1 = m_Model.STCInfo.BaseWave1,
            Base2 = m_Model.STCInfo.BaseWave2,
            Trans = e.Trans,
            ABS = dRetABS,
            Conc = dRetConc,
            Factor = m_Model.Factor,
            AVG = dRetABS,
            STD = dRetConc,
        };

        await Device.InvokeOnMainThreadAsync(() =>
        {
            m_Model.MeasureDataCollection.Add(data);
            // 同样,ObservableCollection可自动刷新,无需手动调用RefreshData
            // xDataGrid.RefreshData();
        });

        m_nCurRow++;
    }
});

最后在TCP数据接收的回调中,将数据发送到Channel:

// 将接收的参数传入队列
await _dataChannel.Writer.WriteAsync((e, dRetABS, dRetConc));

额外优化建议

如果MeasureDataCollection是ObservableCollection<T>,添加元素时会自动触发UI更新,完全可以去掉xDataGrid.RefreshData()调用,减少UI线程的不必要开销。

内容的提问来源于stack exchange,提问作者하하파파

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 02:47:02