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

C#中MessagePack序列化结构体列表追加写入二进制文件的问题

关于MessagePack序列化时序数据到磁盘的问题解答

首先咱们先解决你当前遇到的文件大小为0字节的问题——你的代码里犯了一个很容易忽略的小错误:你生成了序列化后的byte[],但根本没把它写入到FileStream里!对比BinaryFormatter的代码,它是直接把对象序列化到流里,而你的MessagePack代码只完成了序列化步骤,却漏了写入流的操作。

修复当前代码的两种方式

方式1:直接序列化到流(更高效,无需额外byte数组)

public struct struct_realTime 
{ 
    public int indexNum { get; set; } 
    public string currentTime { get; set; } 
    public string currentType { get; set; } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
        List<struct_realTime> list_temp = new List<struct_realTime>(100000); 
        for (int num=0; num < 100000; num++) 
        { 
            list_temp.Add(new struct_realTime { indexNum = 1, currentTime = "time", currentType = "type" }); 
        } 
        string filename = "file.bin"; 
        // 直接序列化到FileStream,跳过byte数组环节
        using (var fileStream = new FileStream(filename, FileMode.Append, FileAccess.Write)) 
        { 
            MessagePackSerializer.Serialize(fileStream, list_temp);
            // 如果你需要验证,可以把流指针移到开头再反序列化测试
            // fileStream.Position = 0;
            // var testList = MessagePackSerializer.Deserialize<List<struct_realTime>>(fileStream);
        } 
    } 
}

方式2:先序列化到byte数组,再写入流

如果你确实需要先拿到byte数组做其他处理,记得加上写入流的代码:

using (var fileStream = new FileStream(filename, FileMode.Append, FileAccess.Write)) 
{ 
    byte[] bytes = MessagePackSerializer.Serialize(list_temp); 
    Console.WriteLine(MessagePackSerializer.ToJson(bytes)); 
    // 关键:把byte数组写入流
    fileStream.Write(bytes, 0, bytes.Length);
}

问题1:异步序列化写入是否更优?

在你这个实时接收时序数据+定期批量写入的场景下,异步操作绝对是更优的选择。原因很简单:

  • 实时数据接收不能被阻塞,如果用同步写入,当磁盘IO比较慢的时候,主线程会卡在写入操作上,可能导致后续的实时数据无法及时接收,甚至丢数据。
  • 异步操作会把序列化和磁盘IO放到后台线程执行,主线程可以继续专注于接收新的时序数据,保证数据接收的实时性。
  • 对于批量数据(比如100条一次),异步IO的性能优势会更明显,尤其是在高并发或者磁盘压力大的环境下。

问题2:如何用MessagePack实现简易磁盘写入(含异步版本)

这里给你一个更贴合你需求的实现——定期收集数据,达到阈值后异步写入磁盘,同时注意MessagePack的配置(建议给结构体加上[MessagePackObject]属性,避免序列化异常,尤其是复杂结构体):

第一步:给结构体添加MessagePack标记(可选但推荐)

[MessagePackObject]
public struct struct_realTime 
{ 
    [Key(0)]
    public int indexNum { get; set; } 
    [Key(1)]
    public string currentTime { get; set; } 
    [Key(2)]
    public string currentType { get; set; } 
}

如果你不想手动加标记,可以使用ContractlessStandardResolver,但显式标记的序列化性能更好,也更稳定。

第二步:异步批量写入的实现示例

class Program
{
    private static readonly List<struct_realTime> _dataBuffer = new List<struct_realTime>(100);
    private const int BatchSize = 100;
    private static readonly string _filePath = "timedata.bin";

    static async Task Main(string[] args)
    {
        // 模拟实时接收数据的循环
        for (int i = 0; i < 1000; i++)
        {
            // 接收一条实时数据
            var newData = new struct_realTime
            {
                indexNum = i,
                currentTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"),
                currentType = "type_" + (i % 5)
            };

            lock (_dataBuffer)
            {
                _dataBuffer.Add(newData);
                // 达到批量阈值,触发异步写入
                if (_dataBuffer.Count >= BatchSize)
                {
                    // 复制一份数据,避免写入时被修改
                    var batchData = _dataBuffer.ToList();
                    _dataBuffer.Clear();
                    // 异步写入,不阻塞主线程
                    await WriteBatchToDiskAsync(batchData);
                    Console.WriteLine($"已写入{BatchSize}条数据");
                }
            }

            // 模拟数据接收间隔
            await Task.Delay(10);
        }

        // 程序结束前,写入剩余的不足一批的数据
        lock (_dataBuffer)
        {
            if (_dataBuffer.Count > 0)
            {
                await WriteBatchToDiskAsync(_dataBuffer.ToList());
                Console.WriteLine($"已写入剩余{_dataBuffer.Count}条数据");
            }
        }
    }

    private static async Task WriteBatchToDiskAsync(List<struct_realTime> batchData)
    {
        try
        {
            // 使用异步FileStream打开文件,追加模式
            using (var fileStream = new FileStream(_filePath, FileMode.Append, FileAccess.Write, FileShare.Read, 4096, true))
            {
                // 异步序列化到流
                await MessagePackSerializer.SerializeAsync(fileStream, batchData);
                // 确保数据写入磁盘(可选,根据可靠性需求)
                await fileStream.FlushAsync();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"写入失败:{ex.Message}");
            // 这里可以根据需求处理失败,比如把失败的数据缓存起来重试
        }
    }
}

这个实现的关键点:

  1. 用lock保护数据缓冲区,因为实时接收和批量写入可能在不同线程执行。
  2. 异步写入时复制缓冲区数据,避免原列表被修改导致序列化异常。
  3. 使用SerializeAsync和异步FileStream,完全不阻塞主线程。
  4. 程序结束时处理剩余数据,避免丢数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:36:00