在多线程同时绘图时,绘制内容稍多时,极易发生撞车,报 Object is in use 错误。
多线程绘图有两种:一为新线程会覆盖老线程。也就是说,新线程创建后,老线程应该作废。
二为新线程和老线程分工独立,两个都要绘制。前一种情形下的处理本应调用 aThread.Abort() 后执行 bThread,然而,Abort 消耗时间很长,画面将不流畅。我目前借助一个全局流水 id, 启动 bThread 时,给线程分配一个新 id, 线程内每步都检查它拿到的 id 是否已经小于当前全局 id, 不检查便退出。
这个方法可以达到目的,但很麻烦,每绘制一步需要检查一次。当需要进一步封装时,由于可能已超出 id 所在的可见域,该方法会失效。后一种情形,似可借助线程队列来做,情况更显复杂。在动手写代码前咨询一下大侠,有没有更好的办法处理多线程抢 Graphics 的问题。谢谢。

解决方案 »

  1.   

    另外,检查 graphics 在占用该如何检查?
      

  2.   

    个人认为这个跟多线程没有关系,这个和Graphic有关。如果你用的this.CreateGraphic()去生成
    你的graphic对象那么在多线程的时候肯定回发生句柄错误“used in ortherwise...等等错误”
    建议把你的画图方法写在Paint函数下。在外部调用的时候时候使用Invalidata()方法。这用就不会出现你所担心的问题了
      

  3.   

    总算有人发言了。我使用的是双缓冲,OnPaint 只是简单的 Render()。抢 graphics 是指抢离屏 graphics。
      

  4.   

    为什么不用互斥呢,参看
    http://blog.csdn.net/knight94/archive/2006/03/22/632512.aspx方法是一样的,不过转换成画图而已
      

  5.   


    用lock锁定你要绘制的对象,这样就能实现线程间互斥了
      

  6.   

    两位说的都有道理:
    lock 之后的确会形成一个等待,适合第一种情形的处理。
    互斥体有单件的效果,可以应付第二种情形。因为没想到 lock (VB.net 叫 SyncLock,汗)我自己做了一个类,亦实现了相似功能,一个类属 bool 变量 + sleep 实现 lock 的效果,当然,比起语言级别的实现来说显得笨拙很多;一个静态成员实现互斥体的效果,比互斥体效果好,具体细节不再赘述。但在实际运用时,发现依然有问题。
    比方我地图上有精灵,现在地图和精灵两个离屏都可以如愿以偿的绘制,不再出抢 Graphics 用的错误。但如果地图控件本身有一些控件显示隐藏的操作,会导致地图重绘,此时依然会引发 object in use 错误。关于这个问题,我想需要探知 graphics 可否使用(可否 getHdc)才是最终办法。
      

  7.   

    不是你可以用Graphics.GetHDC获得hdc,但是还是会出现上面的现象
      

  8.   

    sorry
    你可以用Graphics.GetHDC获得hdc,但是还是会出现上面的现象
      

  9.   

    获得到才怪,原以为能否获得 dc 是不出错的标准,谁知连 gethdc 都出那个异常
    不过也好,这样不用去找检验 dc 能用的函数了。
      

  10.   

    noky(孤狼傲血) 的方法是对的
    这个是标准的windows体系的消息处理方式,其实就是只有一个线程来处理画图
    如果要多个线程都来画图,这个只能发生冲突
    要么就是创建互斥量,你自己写的类基本上肯定不行的,本身就会发生不能互斥的情况,因为在多个线程当中进行
      

  11.   

    noky(孤狼傲血) ( ) 信誉:100  2006-03-29 12:57:00  得分: 0  
     
     
       个人认为这个跟多线程没有关系,这个和Graphic有关。如果你用的this.CreateGraphic()去生成
    你的graphic对象那么在多线程的时候肯定回发生句柄错误“used in ortherwise...等等错误”
    建议把你的画图方法写在Paint函数下。在外部调用的时候时候使用Invalidata()方法。这用就不会出现你所担心的问题了
      
     
    ================================================================
    我喜欢用这种方法,至于双缓冲,我会用窗体Form类的SetStyle()来让它自动实现双缓冲
    public void EnableDoubleBuffering()
    {
       // Set the value of the double-buffering style bits to true.
       this.SetStyle(ControlStyles.DoubleBuffer | 
          ControlStyles.UserPaint | 
          ControlStyles.AllPaintingInWmPaint,
          true);
       this.UpdateStyles();
    }
      

  12.   

    汗,lock 和我的那个类似 lock 的办法都不行。由于 lock (等待)事实上形成一个队列,先进先出,所以每一个家伙都要渲染一次,这样地图拖动后,上面的精灵(渲染较慢)要重绘多次,导致不断闪烁。
    现在记起为什么以前用流水号,以前那个用流水号的办法可以解决这个问题,因为新的线程需要立刻取代老的线程。
      

  13.   

    littlegang(Gang) ( ) 信誉:100  2006-03-29 17:34:00  得分: 0  
     
     
       noky(孤狼傲血) 的方法是对的
    这个是标准的windows体系的消息处理方式,其实就是只有一个线程来处理画图
    如果要多个线程都来画图,这个只能发生冲突
    要么就是创建互斥量,你自己写的类基本上肯定不行的,本身就会发生不能互斥的情况,因为在多个线程当中进行不能用互斥体,假如我窗口上要放两个地图控件,我这两个控件都在渲染,用互斥体会发生甲控件锁乙控件的情况。我用了一个 static Dictionary<control,bool>。
      

  14.   

    我喜欢用这种方法,至于双缓冲,我会用窗体Form类的SetStyle()来让它自动实现双缓冲
    public void EnableDoubleBuffering()
    {
       // Set the value of the double-buffering style bits to true.
       this.SetStyle(ControlStyles.DoubleBuffer | 
          ControlStyles.UserPaint | 
          ControlStyles.AllPaintingInWmPaint,
          true);
       this.UpdateStyles();
    }恩,.net 2.0 包含了 BufferGraphics 的处理。通过代码,可以申请更多数目的离屏。通过什么方式实现双缓冲,并不是问题的关键。
      

  15.   

    你地图上的控件是否是你自己开发的,或者你时候能比较轻松的控制,如果可以的话,那么在程序进入互斥的地方,暂时关闭控件的功能,等操作完了再打开;至于你程序这部分,要加异常处理,防止控件在操作的时候,程序也去操作。不过上面的方法都不是最好的方法,你最好重写一下容器,例如,如果地图放在picturebox上,那么你重写一个picturebox,然后重载其PreProcessMessage事件,可能更好。
      

  16.   

    恩,有道理,可以尝试一下 PreProcessMessage,我的地图重载的是一个 Panel。
      

  17.   

    你和我碰到的问题一样,都是draw的时候速度慢,我的主要是数据量特别大,正想办法解决中