none
后台线程中大量串口操作导致界面主线程假死 RRS feed

  • 问题

  • 我有一个第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
    2011年4月18日 17:18

答案

  • 通过自身的努力我终于解决了问题。原来这是一个COM组件与线程单元模型的问题。我将Main方法线程模型由STA改为MTA后问题就解决了。初步分析原因如下:

    在STA模式下,在一个线程中创建的所有组件都不能被其它线程访问。WinForm默认为STA模式,在这种模式下,创建后台线程同时意味着创建了新的STA单元。而我的COM组件就运行在该单元,而它又是一个极度耗时操作,且有可能又创建了其它什么东西,以致于在线程调度时主线程和其它线程都得不到CPU时间,因此假死。COM组件运行完毕释放所有资源后主线程恢复正常。

    不知道这种解释是否正确,希望专家们能给出权威的说明。谢谢。

    • 已标记为答案 廖斌 2011年4月19日 15:52
    2011年4月19日 15:52

全部回复

  • 版主们、专家们、MVP们快来帮帮忙啊,我都等了一天了。

    2011年4月19日 7:02
  • 你好,请问你的IDE是什么版本?具有性能测试工具么?可以使用IDE的性能测试工具或者windows自带的性能坚持工具查看下到底是什么操作使你你的UI这么卡。是否是因为你使用了不恰当的委托呢?

    我的意思是:当你其他线程操作后,你使用委托去更新您的UI,当其他线程操作快过你的UI更新操作,导致了主线程的堵塞。

    2011年4月19日 8:33
  • 太感动了,终于有人回应了。先行谢过。

    我是在vs2010中的C# winform项目中使用该COM组件的。目前最困扰我的问题是我根本没有更新UI,只是单纯地在后台线程中进行串口操作。主线程就挂了。

    性能测试工具可以看到COM组件内部的什么操作使UI卡吗?有没有这方面的资料啊

    谢谢

    2011年4月19日 8:58
  • dear
    并非使用Thread就表示天下无,还是有些观念需要了解,比如回报UI时是有有大量资料或太过频繁
    下列连结为Winform的入门观念,你可参考看看

    http://blog.darkthread.net/blogs/darkthreadtw/archive/2008/03/26/better-winform-ui.aspx


    秘訣無它,唯勤而已 http://www.dotblogs.com.tw/yc421206/
    2011年4月19日 9:12
  • 终于等到MVP来了,thanks god

    过于频繁的回报进度给UI的确会使UI无法响应,这一点我十分清楚。

    但是我已经说过,第1步,我只在后台线程调用COM组件,不回报进度给UI,主线程还是被阻塞。

    2011年4月19日 9:26
  • dear 可否上传源码方便诊断~
    秘訣無它,唯勤而已 http://www.dotblogs.com.tw/yc421206/
    2011年4月19日 10:04
  •  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());就是大量数据从串口中通过,也是这一句导致同进程中其它所有线程阻塞。
    2011年4月19日 11:15
  • 通过自身的努力我终于解决了问题。原来这是一个COM组件与线程单元模型的问题。我将Main方法线程模型由STA改为MTA后问题就解决了。初步分析原因如下:

    在STA模式下,在一个线程中创建的所有组件都不能被其它线程访问。WinForm默认为STA模式,在这种模式下,创建后台线程同时意味着创建了新的STA单元。而我的COM组件就运行在该单元,而它又是一个极度耗时操作,且有可能又创建了其它什么东西,以致于在线程调度时主线程和其它线程都得不到CPU时间,因此假死。COM组件运行完毕释放所有资源后主线程恢复正常。

    不知道这种解释是否正确,希望专家们能给出权威的说明。谢谢。

    • 已标记为答案 廖斌 2011年4月19日 15:52
    2011年4月19日 15:52