IBM MQ:使用CCDT(通道定义表)文件时出现访问冲突错误
MQ CCDT多线程访问引发System.AccessViolationException异常解决方案
我们通过通道定义文件(CCDT)根据队列管理器名称获取主机名和通道,但偶尔会触发未处理异常导致进程终止,怀疑是多线程同时访问引发的问题。
环境信息
- 框架版本:v4.0.30319
- CCDT文件类型:二进制
- .NET应用运行模式:托管模式
- MQ客户端软件版本:9.2.0.6
- 创建CCDT文件的MQ版本:8.x
- CCDT配置方式:通过命令行设置系统环境变量:
setx MQCHLLIB C:\test\ /M setx MQCHLTAB cctdtest.TAB /M
异常信息
描述:进程因未处理异常而终止。
异常信息:System.AccessViolationException
发生位置:at System.Runtime.InteropServices.Marshal.PtrToStructureHelper(IntPtr, System.Object, Boolean)
代码片段
var connectionProperties = new Hashtable(); connectionProperties.Add(MQC.TRANSPORT_PROPERTY, MQC.TRANSPORT_MQSERIES_MANAGED); // 网络异常时重连到同一队列管理器 if (!string.IsNullOrEmpty(MQHostName)) { connectionProperties.Add(MQC.CONNECT_OPTIONS_PROPERTY, MQC.MQCNO_RECONNECT_Q_MGR); connectionProperties.Add(MQC.CONNECTION_NAME_PROPERTY, String.Format("{0}({1})", MQHostName, MQPort)); } // 创建入站连接用于读取消息 if (MQQueueNameIn != null && MQQueueNameIn.Length > 0) { queueManagerIn = new MQQueueManager(MQManagerName, connectionProperties); queueIn = queueManagerIn.AccessQueue(MQQueueNameIn, MQC.MQOO_BROWSE + MQC.MQOO_INPUT_SHARED + MQC.MQOO_FAIL_IF_QUIESCING + MQC.MQOO_INQUIRE); }
解决方案
1. 强制CCDT访问的线程安全
MQ托管客户端多线程访问CCDT时存在线程安全隐患,需对队列管理器实例化逻辑加全局锁:
private static readonly object _mqInitLock = new object(); // 创建连接时加锁 lock (_mqInitLock) { queueManagerIn = new MQQueueManager(MQManagerName, connectionProperties); queueIn = queueManagerIn.AccessQueue(MQQueueNameIn, MQC.MQOO_BROWSE + MQC.MQOO_INPUT_SHARED + MQC.MQOO_FAIL_IF_QUIESCING + MQC.MQOO_INQUIRE); }
2. 升级MQ客户端版本
MQ 9.2.0.6存在已知的CCDT多线程访问内存异常问题,建议升级到9.2.0.10及以上修复版本,官方已针对性修复该类内存访问错误。
3. 移除混合配置冲突
代码中同时存在CCDT环境变量配置和手动指定CONNECTION_NAME_PROPERTY的逻辑,会导致客户端内部解析逻辑冲突。如果依赖CCDT获取连接信息,应移除手动指定连接参数的代码:
var connectionProperties = new Hashtable(); connectionProperties.Add(MQC.TRANSPORT_PROPERTY, MQC.TRANSPORT_MQSERIES_MANAGED); // 创建入站连接用于读取消息 if (MQQueueNameIn != null && MQQueueNameIn.Length > 0) { lock (_mqInitLock) { queueManagerIn = new MQQueueManager(MQManagerName, connectionProperties); queueIn = queueManagerIn.AccessQueue(MQQueueNameIn, MQC.MQOO_BROWSE + MQC.MQOO_INPUT_SHARED + MQC.MQOO_FAIL_IF_QUIESCING + MQC.MQOO_INQUIRE); } }
4. 捕获致命异常避免进程终止
在.NET 4.0及以上版本,可通过特性捕获AccessViolationException,记录日志并尝试恢复:
[HandleProcessCorruptedStateExceptions] [SecurityCritical] public void InitMQConnection() { try { lock (_mqInitLock) { queueManagerIn = new MQQueueManager(MQManagerName, connectionProperties); queueIn = queueManagerIn.AccessQueue(MQQueueNameIn, MQC.MQOO_BROWSE + MQC.MQOO_INPUT_SHARED + MQC.MQOO_FAIL_IF_QUIESCING + MQC.MQOO_INQUIRE); } } catch (AccessViolationException ex) { // 记录异常日志 // 清理资源 queueIn?.Close(); queueManagerIn?.Disconnect(); queueManagerIn = null; queueIn = null; // 可选:添加重试逻辑 } }
5. 验证CCDT文件兼容性
用MQ 9.2版本重新生成CCDT文件,避免MQ 8.x生成的二进制CCDT与9.2.0.6客户端存在解析兼容性问题。
内容的提问来源于stack exchange,提问作者b_patil
相关产品推荐
相关产品推荐

