积极答复者
后台线程中大量串口操作导致界面主线程假死

问题
-
我有一个第3方的COM组件,其作用是使用串口发送和接收大量数据。
我在一个WinForm应用程序中分别使用了如下两种方式调用该COM组件
1、在backgroundworker中
2、在Thread中
但是,当COM组件开始发送和接收数据少许时间后,界面出现假死现象。
具体症状为:
1、鼠标移动到Form内容范围时呈等待状,
2、此时点击Form会使标题栏出现无法响应字样
3、但仍然可以拖动该Form
4、此时串口上的发送与接收数据正常进行
5、当串口操作完成后,界面恢复正常
经过研究确认,在backgroundworker或者Thread中进行其它耗时的操作都不会使界面假死,但大量串口操作即便是放在优先级最低的线程中都会使界面假死。
请教专家们,在大量串口操作时,怎么样才能使界面不会假死?我已经是在后台线程或backgroundworker中操作串口的了
这篇贴子http://social.msdn.microsoft.com/Forums/zh-CN/visualcshartzhchs/thread/098cb0d3-c4b8-4456-9c63-4009bd0b2c63/我已看过,不解决问题
- 已更改类型 廖斌 2011年4月19日 7:00
答案
-
通过自身的努力我终于解决了问题。原来这是一个COM组件与线程单元模型的问题。我将Main方法线程模型由STA改为MTA后问题就解决了。初步分析原因如下:
在STA模式下,在一个线程中创建的所有组件都不能被其它线程访问。WinForm默认为STA模式,在这种模式下,创建后台线程同时意味着创建了新的STA单元。而我的COM组件就运行在该单元,而它又是一个极度耗时操作,且有可能又创建了其它什么东西,以致于在线程调度时主线程和其它线程都得不到CPU时间,因此假死。COM组件运行完毕释放所有资源后主线程恢复正常。
不知道这种解释是否正确,希望专家们能给出权威的说明。谢谢。
- 已标记为答案 廖斌 2011年4月19日 15:52
全部回复
-
public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { backgroundWorker1.RunWorkerAsync(); } private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) { // 类BootModeIntegOpr是从COM组件的tlb中导出的 BootModeIntegOpr bootModePlatFormHandler = new BootModeIntegOpr(); byte[] buffer =File.ReadAllBytes("test.bin"); BootModeFile file = new BootModeFile(); file.Length = buffer.Length; file.Address = (int)0x30000000; file.Data = Marshal.AllocCoTaskMem(file.Length); Marshal.Copy(buffer, 0, file.Data, file.Length); file.MaxLength = 0x800; ptr = ToIntPtr(file); <em><span style="text-decoration:underline"><strong>result = bootModePlatFormHandler.Download(ptr.ToInt32()); </strong></span></em> if (result == 0) { Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); } Marshal.FreeCoTaskMem(file.Data); Marshal.FreeCoTaskMem(ptr); } private static IntPtr ToIntPtr(object o) { IntPtr ptr = Marshal.AllocCoTaskMem(Marshal.SizeOf(o)); Marshal.StructureToPtr(o, ptr, true); return ptr; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] internal struct BootModeFile { /// <summary> /// 文件长度 /// </summary> internal int Length; /// <summary> /// 下载地址 /// </summary> internal int Address; internal IntPtr Data; internal int BufferLength; /// <summary> /// 文件类型 /// </summary> [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] internal string Type; /// <summary> /// 文件名 /// </summary> [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] internal string Name; /// <summary> /// 缓冲内容是否来自于文件 /// </summary> internal bool FromFile; /// <summary> /// 一次操作的最大长度 /// </summary> internal int MaxLength; internal IntPtr FDLCode; internal IntPtr FDLCodeMapView; /// <summary> /// 是否改变缓冲区中的内容 /// </summary> internal bool IsChanged; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] internal string Id; } } }
代码中result = bootModePlatFormHandler.Download(ptr.ToInt32());就是大量数据从串口中通过,也是这一句导致同进程中其它所有线程阻塞。 -
通过自身的努力我终于解决了问题。原来这是一个COM组件与线程单元模型的问题。我将Main方法线程模型由STA改为MTA后问题就解决了。初步分析原因如下:
在STA模式下,在一个线程中创建的所有组件都不能被其它线程访问。WinForm默认为STA模式,在这种模式下,创建后台线程同时意味着创建了新的STA单元。而我的COM组件就运行在该单元,而它又是一个极度耗时操作,且有可能又创建了其它什么东西,以致于在线程调度时主线程和其它线程都得不到CPU时间,因此假死。COM组件运行完毕释放所有资源后主线程恢复正常。
不知道这种解释是否正确,希望专家们能给出权威的说明。谢谢。
- 已标记为答案 廖斌 2011年4月19日 15:52