孤独的猫

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 :: 管理 ::

Delphi多线程学习(10):Label(VCL)同步的问题

     上文中,多线程同步主窗体的Label的Caption属性值,发现一个问题:使用Synchronize用于同步的时候,主窗体好像死掉一样;而直接用子程序为Label的引用赋值,则有时会出现“Canvas  does not allow drawing”错误。书上说VCL同步一定要用Synchronize,而不能直接访问。

    测试:

   {主窗体}  
  1. unit Unit2;  
  2.    
  3. interface  
  4.    
  5. uses  
  6.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,  
  7.   Dialogs, StdCtrls;  
  8.    
  9. type  
  10.   TForm2 = class(TForm)  
  11.     Label1: TLabel;  
  12.     Button1: TButton;  
  13.     Label2: TLabel;  
  14.     Label3: TLabel;  
  15.     procedure Button1Click(Sender: TObject);  
  16.   private  
  17.     { Private declarations }  
  18.   public  
  19.     { Public declarations }  
  20.   end;  
  21.    
  22. var  
  23.   Form2: TForm2;  
  24.    
  25. implementation  
  26.    
  27. uses TestThread;  
  28.    
  29. {$R *.dfm}  
  30.    
  31. procedure TForm2.Button1Click(Sender: TObject);  
  32. begin  
  33.   TTestThread.Create(Label1);  
  34.   //TTestThread.Create(Label2);   
  35.   //TTestThread.Create(Label3);   
  36. end;  
  37.    
  38. end.{多线程类}  
  39. unit TestThread;  
  40.    
  41. interface  
  42.    
  43. uses  
  44.   Classes,StdCtrls;  
  45.    
  46. type  
  47.   TTestThread = class(TThread)  
  48.   private  
  49.     { Private declarations }  
  50.     FLabel:TLabel;  
  51.     Fstr:string;  
  52.     procedure UpdateLabel;  
  53.   protected  
  54.     procedure Execute; override;  
  55.   public  
  56.     constructor Create(Lab:TLabel);  
  57.   end;  
  58.    
  59. implementation  
  60.    
  61. uses Unit2,SysUtils,windows;  
  62.    
  63. { TTestThread }  
  64.    
  65. constructor TTestThread.Create(Lab: TLabel);  
  66. begin  
  67.   FLabel:=Lab;  
  68.   Inherited Create(False);  
  69.   FreeOnTerminate:=True;  
  70. end;  
  71.    
  72. procedure TTestThread.Execute;  
  73. var  
  74.   i:Integer;  
  75. begin  
  76.   { Place thread code here }  
  77.   for i := 0 to 20000 do  
  78.     begin  
  79.       if Not Terminated then  
  80.         begin  
  81.           Fstr:=Format('线程ID:%d,第%d个循环',[GetCurrentThreadID,i]);  
  82.           //UpdateLabel;   
  83.           Synchronize(UpdateLabel);  
  84.         end;  
  85.     end;  
  86.    
  87. end;  
  88.    
  89. procedure TTestThread.UpdateLabel;  
  90. begin  
  91.   FLabel.Caption:=Fstr;  
  92. end;  
  93.    
  94. end.  

 

 

经过测试,只创建一个线程,用Synchronize同步主窗体的一个Label,那么程序无问题,可以见到同步的过程。当同时创建三个线程,用Synchronize同时同步主窗体的三个Label,那么主窗体好似死了一样,过了好久才显示最后循环的同步值。(我的机子很差,三个线程就卡了,你可以多创建几个线程测试卡死的效果。)

     可能原因,因为Synchronize中的代码是在主窗体线程中执行的,同时几个线程要求主窗体执行显示Label,所以主窗体可能忙不过来。

     解决办法:在Synchronize(UpdateLabel);语句之后加上Sleep(0);,那么可以了。Sleep(0)是指CPU交出当前线程的执行权,让CPU去执行其他线程。也就是放弃当前线程的时间片,转而执行其他线程。一般来说,如果当前线程比较耗时比较占CPU资源,可以在结尾处加上Sleep(0), 这样效率会得到大大的提高。

     加上Sleep(00测试了一下,完全成功没问题。 所以前面几篇的代码中,应该照此修改。

posted on 2012-03-06 20:30  孤独的猫  阅读(1730)  评论(0)    收藏  举报