我做了一个多窗体多线程通讯程序,主线程用Mscomm定时收数据,一线程实现动态绘图(不止一条线),一线程用Socket实现局域网通讯。由窗体的OnCreate事件启动定时收数据,同时创建绘图线程,因为数据接收频率高,绘图不需要那么高频率,所以想到用定时器来定时唤醒绘图线程,为防止绘图线程一直运行(我试过),一次画完后我把绘图线程挂起,可是这样一来绘图线程根本不执行。
 各位帮我看看,该怎么改,(老板一定要用多线程做)
 相关代码如下:
procedure TForm_show.FormCreate(Sender: TObject); //窗体产生
begin
.
.
Thread_Wen:=TDrawThread.Create(DrawGrid1,clRed,tpnormal,X1,Y1,X2,Y2);
end;procedure TForm_show.Timer_WenThrTimer(Sender: TObject);  {温度线程启动}
begin
   if ThrStart_Wen=1 then
     begin
       F_Wen:=StrToFloat(Edit_Wen.Text);
       X1:=X2_Wen;
       Y1:=Y2_Wen;
       X2:=X1+Step_Wen;
       Y2:=Y0-Round(scale_Wen*F_Wen);
       X2_Wen:=X2;
       Y2_Wen:=Y2;
      Thread_Wen.Resume;
       Thread_Wen.Suspend;
       end;
   else
     Thread_Wen.Terminate;
end;
unit DrThread;interfaceuses
  Classes,Graphics,Grids;type
  TDrawThread = class(TThread)
  private
    { Private declarations }
  protected
    procedure Execute; override;
    procedure Draw;
  public
    FColor:TColor;
    FP:TThreadPriority;
    X1s,Y1s,X2s,Y2s:integer;
    FDrawGrid:TDrawGrid;
    Constructor Create(DrawGr:TDrawGrid;Col:TColor;P:TThreadPriority;X1s,Y1s,X2s,Y2s:integer);
  end;implementation
 uses show;Constructor TDrawThread.Create(drawGr:TDrawGrid;Col:TColor;P:TThreadPriority;X1s,Y1s,X2s,Y2s:integer);
begin
  FDrawGrid:=DrawGr;
  FColor:=Col;
  X1:=X1s;
  Y1:=Y1s;
  X2:=X2s;
  Y2:=Y2s;
  fp:=p;
  inherited Create(true);
  Priority:=fp;
end;procedure TDrawThread.Draw;
begin
  with FDrawGrid.canvas do
    begin
      lock;
      Pen.Color:=FColor;
      MoveTo(X1,Y1);
      LineTo(X2,Y2);
      unlock;
    end;
end;procedure TDrawThread.Execute;
begin
   synchronize(draw);
    freeonterminate:=true;
end;end.

解决方案 »

  1.   

    止绘图线程一直运行(我试过),一次画完后我把绘图线程挂起,可是这样一来绘图线程根本不执行。
    你都把绘图线程杀死了freeonterminate:=true;
    请大家去 http://www.new7wonders.com/c/voting.php 投长城一票
      

  2.   

    不会吧,定时器每触发一次,线程都要创建一次。
    建议:
        窗体中创建线程,在窗体Destroy方法中释放线程,每次定时器触发调用
    Thread_Wen.Resume;
    线程中Execute方法改为
    while not terminated do
    begin
    ...
        suspend;
    end;
      

  3.   

    to pwlsjm() :
      按您建议试了,仍然是绘图线程一直运行,一直在画,线程挂不起来。
      而且多曲线绘制时,根本不行,线是乱的。
      

  4.   

    一直在画?
    你开了多少个线程在画?
    把定时器那部分代码去掉。只留一个线程画就是了。
    另外,如果觉得线程suspend再唤醒有些慢, 可以改用TEvent来等待。思路理清,问题可以一个个处理好的。
      

  5.   

    to halfdream(哈欠):
    把定时器那部分代码去掉,那定时绘图线程在哪儿唤醒呢?
      

  6.   

    我的情况和你有点相似,我的服务器程序有可能在短时间里接收到很多udp数据,如果一个个处理的话肯定来不及。所以想了这么一个方法。你的情况比我好一点。
      

  7.   

    procedure TDrawThread.Execute;
    begin
      //这里要进入循环,不进循环,在Form中控制线程的Resume, Suspend都是假的
      while not Terminated do
        synchronize(draw);
      freeonterminate:=true;
    end;
      

  8.   

    to copy_paste(木石三) :
      这段我使用过,在OnTimer里Resume线程,只执行一次。
    to  fsb2001(涵秋) :
      恐怕不行,收数据频率较绘图频率高得多,
      

  9.   

    我的看法是,你的想法是对的(但你的做法,由Timer触发的线程肯定一直在运行,可以在Resume和Supend中加入适当的同步控制代码以保证线程不乱画线),不过我对X1,X2,Y1,Y2有些不明白,它是全局变量吗? 如果是那你用Timer来改它们的值是不对的,因为线程不能与Timer同步,线程中得到的X1,X2,Y1,Y2是随机的,肯定会乱画线啦。
    比较好的做法是用两个线程,一个测数据,写入一个数据缓冲区,用另一个线程来画线,两个线程用同一个缓冲区的数据,通过线程来保证来数据的完整。
    这是一个明显的双线程同步的问题。很多相关的书都有介绍。
      

  10.   

    同意fsb2001(涵秋) 的建议, 不过senliu(翠儿) 目前方式处理也不算什么错,
    senliu(翠儿),把关于Timer部分的代码全部统统去掉,既然用了线程,就不要用Timer来凑热闹。zyc(zir) 写的话仔细看看, 希望能让你快速找到错误所在。
      

  11.   

    先谢谢各位。
    根据各位意见,我重做了,在窗体创建中创建线程,主线程收数据写入文本(以前做的),去掉Timer,新添变量P,用于统计收到数据个数,每收5组数据唤醒绘图线
    程画一次图,试了还是不行,根本不画。代码如下:
    procedure TForm_show.FormCreate(Sender: TObject); //窗体产生
    begin
    .
    .
    Thread_Wen:=TDrawThread.Create(DrawGrid1,clRed,tpnormal,X1,Y1,X2,Y2);
    end;procedure TForm_show.Timer_StartTimer(Sender: TObject);  {发送地址}
    begin
       if k<=I then
        begin
          Mscomm1.output:=inttostr(MyAddress[k]);
          k:=k+1;
        end
      else
        begin
          k:=1;
          Mscomm1.output:=inttostr(MyAddress[k]);
          k:=k+1;
        end;
        p:=P+1;
    end;procedure TForm_show.MSComm1Comm(Sender: TObject); {收数据}
    var
       InputString:string;
    begin
       Inputstring:=Mscomm1.Input;
       if k=2 then
         begin
           Writeln(MyTextFile,DateToStr(time));   //
           Writeln(MyTextFile,InputString);
          end
       else
         Writeln(MyTextFile,InputString);
       case MyAddress[k-1] of
        1: begin
           edit_Time.text:=timetostr(time);  //在窗体编辑框显示时间和数据
             edit_Wen.Text:=inputstring;
             if p=5*I then        //每收5组数据画一次图
               begin
                 X1:=X2_Wen;
                 Y1:=Y2_Wen;
                 F_Wen:=StrToFloat(inputstring);
                 X2:=X1+Step_Wen;
                 Y2:=Y0-Round(scale_Wen*F_Wen);
                 X2_Wen:=X2;
                 Y2_Wen:=Y2;
                 Thread_Wen.Resume;    //唤醒绘图线程
                 p:=1;
               end;
           end;
        2:edit_Ye.text:=inputstring;
        3:edit_Jing.text:=inputstring;
        4:edit_Chu.text:=inputstring;
       end;
    end;
    unit DrThread;interfaceuses
      Classes,Graphics,Grids;type
      TDrawThread = class(TThread)
      private
        { Private declarations }
      protected
        procedure Execute; override;
        procedure Draw;
      public
        FColor:TColor;
        FP:TThreadPriority;
        X1s,Y1s,X2s,Y2s:integer;
        FDrawGrid:TDrawGrid;
        Constructor Create(DrawGr:TDrawGrid;Col:TColor;P:TThreadPriority;X1s,Y1s,X2s,Y2s:integer);
      end;implementation
      uses show;{ TDrawThread }
    Constructor TDrawThread.Create(drawGr:TDrawGrid;Col:TColor;P:TThreadPriority;X1s,Y1s,X2s,Y2s:integer);
    begin
      FDrawGrid:=DrawGr;
      FColor:=Col;
      X1:=X1s;
      Y1:=Y1s;
      X2:=X2s;
      Y2:=Y2s;
      fp:=p;
      inherited Create(true);
      Priority:=fp;
    end;procedure TDrawThread.Draw;
    begin
      with FDrawGrid.canvas do
        begin
          lock;
          Pen.Color:=FColor;
          MoveTo(X1,Y1);
          LineTo(X2,Y2);
          unlock;
        end;
    end;procedure TDrawThread.Execute;
    begin
       while not terminated do
       begin
       synchronize(draw);
       suspend;
      end;
    end;end.
     
      

  12.   

    FDrawGrid如果在隐藏的窗口上就画不上去,有时候。
      

  13.   

    不是的,formCreat时,已有标注。
    高手们,帮帮我。
      

  14.   

    senliu,你说的情况我也碰到过类似. 我当时的情况是主窗体负责显示状态
    两个线程,一个线程A用于监视目录文件变化, 
    一个线程B用于处理目录文件变化。
    当时我的监视目录线程A在运行期无法启动线程B(奇怪的是在IDE调试期是可以
    通过的).我想问题的关键是应该在于
    监视数据线程在进入接收态时,进入synchronize保护状态了.
    这时候 负责画图的线程是无法更新主窗体上的CANVAS
    有朋友建议我用Event可以做这样的事情.因为我当时的任务比较简单
    用timer(就是你老板禁止用的)办法解决了.如果用Event 事件机制, 在你的监视数据线程内启动一个事件,
    用此事件激活画图线程。但是还需要涉及到临界区的处理
    比较麻烦.附带说一句: tTimer实际上封装了 wm_timer 消息的处理
    wm_timer 发出的频率实际上是由系统中断int1c决定的.
    但是因为它是个消息, 所以 wm_timer并不是可靠定时的.
    也就是说在系统繁忙时, tTimer工作就不会稳定.Delphi的主窗体是程序的主线程. mscomm组件如果工作在异步方式接收数据
    时,应该是单独跑在自己的线程里的(我没有用过mscomm组件,这段理解是想当然)
      

  15.   

    senliu(翠儿) :
    你改的改程序还没有解决问题,你的主Thread和DrawThread之间的数据之间还不能实现同步。
    好的做法是,用个同步量表示数据池中有多少个数据,但对数据池和同步量的读写本用临界区的方法来控制(也可以用PV方法来控制)。这里面,一个数据应该就是一组数据,包括Color、X1、Y1、X2、Y2,数据池的容纳多少组数据可以经过测试。
    主线程循环写入数据,只判定数据池中是否可写数据,不要去控制子线程的运行(否则会出错),子线循环等数据池中的数据,如果池中有一组以上的数据,就画线,否就等,不要supend。用supend和resume不能实现线程同步。