使用ActionBlock/Channel消费FIX行情消息时的数据完整性丢失问题
EPAM FIX引擎异步消费时Tag268丢失的解决方案
问题背景
我用EPAM的FIX引擎做协议通信,通过继承实现void OnNewMessage(FixMessage msg)接收消息。因为这个方法不能执行耗时操作,所以用ActionBlock和Channel把CPU密集型计算卸载到工作线程,但遇到了奇怪的问题:往Channel写消息前明明能拿到Tag268,消费时却提示这个标签不存在,怀疑是线程导致的数据完整性丢失。
核心原因
EPAM的FixMessage对象是线程不安全且被引擎内部复用的。OnNewMessage传入的msg是引擎维护的复用实例,你直接把它丢进Channel后,引擎会在后续消息处理中复用这个对象,覆盖原有数据,等到消费线程读取时,原来的Tag268已经被清除或修改了。
解决办法
1. 写入前深拷贝消息对象
在OnNewMessage里先复制一份完整的FixMessage,再把拷贝后的对象传入Channel,彻底隔离引擎的复用对象和消费对象:
public override void OnNewMessage(FixMessage msg) { // 用拷贝构造函数创建新消息(确认EPAM引擎支持此方式) var clonedMsg = new FixMessage(msg); // 将拷贝后的消息写入Channel _channel.Writer.TryWrite(clonedMsg); }
如果引擎没有提供拷贝构造函数,就手动遍历所有标签,把原消息的键值对逐一复制到新的FixMessage实例中。
2. 序列化后传递文本
把消息序列化为标准FIX格式的字符串,消费时再反序列化为FixMessage,这种方式完全避免对象共享:
// 接收线程 public override void OnNewMessage(FixMessage msg) { string fixRawText = msg.ToString(); _channel.Writer.TryWrite(fixRawText); } // 消费线程处理逻辑 async Task ProcessMessage(string fixText) { var msg = new FixMessage(); msg.FromString(fixText); // 此时可以正常读取Tag268 }
3. 验证对象复用问题
可以在写入和消费时分别打印消息的哈希码和Tag268值:
// 写入前 Console.WriteLine($"写入:MsgHashCode={msg.GetHashCode()}, Tag268={msg.GetTagValue(268)}"); // 消费时 Console.WriteLine($"消费:MsgHashCode={receivedMsg.GetHashCode()}, Tag268={receivedMsg.GetTagValue(268)}");
如果两次哈希码相同,说明是同一个对象被引擎复用了,直接坐实问题根源。
内容的提问来源于stack exchange,提问作者Allstar
相关产品推荐
相关产品推荐

