周期性运行你应该在Execute中
while not Terminated do
begin
    //此处加标志判断是否执行下面语句。
    CallPlcMethodsHelper();
    //如果周期性的执行,这边比如Sleep(1000);是最方便的。
end;你整个方法使用Synchronize,等于没有多线程,因为Synchronize是将方法同步到主线程执行,你应该在你需要操作VCL的地方使用Synchronize方法。临界区请uses SyncObjs;然后使用TCriticalSection类。
要用try...finally块保护起来,防止因为异常导致临界区未释放。
TCriticalSection.Enter();
try
 //这里是业务逻辑
finally
TCriticalSection.Leave();
end;
如果业务逻辑代码可能会产生异常,请再使用try...except块保护,如果不保护,发生异常线程直接挂掉,你临界区自然不会Leave,导致死锁。

解决方案 »

  1.   

    楼上需要自行创建TCriticalSection对象,我只是示例你临界区需要调用的函数。
      

  2.   

    谢谢pathletboy,已经试过了,不再显示未响应了,说明多线程有效果了,(:-我还想追问一下第二点,我在线程里面访问主窗体的变量/对象之类的是否可行,现在是一访问就报Access Violation错,当然有变通的方法是在线程内也重复定义一次,然后再外部赋值/取值,但是因为涉及到的比较多,所以如果能直接访问的话就完美了。另外,线程中没有UI操作,是否就可以不需要使用Synchronize方法,目前都是把所有的数据缓存到TList中。
      

  3.   

    1、线程中无对VCL操作不需要Synchronize
    2、如果你变量/对象是属于线程的尽量放入线程。
    3、2个线程同时操作一块/一个对象时候使用临界区。
    你的问题把你代码发出来看看就知道哪的问题了。
      

  4.   

    没想到这么快就回复了,谢谢。操作的变量/对象都属于主Form的,因为效率问题,把部分读取设备数据的业务放到了线程中,让线程在旁边慢慢读,不影响界面,提升用户体验,只起了一个线程,也用了前面你教的方法TCriticalSection,应该没有影响的。我的代码是这样的:
    主程序EXE中打开一个DLL模块的Form,然后DLL的Form中声明了一个Private FIsDemoMode:boolean;(这里只列出最简单的例子,其实还有很多变量及TList之类的对象),然后在线程的Execute方法中访问FIsDebugMode,FrmEntry是和线程同一单元的Form实例,
    try
        if FrmEntry.FIsDebugMode then StrToInt('2');
        //去掉了其他的业务逻辑
    except
        On E:Exception do FExcutionException:=E; //特别在线程里定义了个异常对象,然后在线程外处理该异常
    end;
    //线程外捕获异常
    with FPlcDataReaderThread do
    begin
        if FExcutionException<>nil then Application.HandleException(FExcutionException);
    end;这样的代码,我拉到一般的exe程序是可以执行的,不知道是不是在DLL里执行的原因。
      

  5.   

    估计是你操作TList的问题,如果你几条线程同时访问TList,必须得“正确”使用临界区,很多人临界区是用了,可惜使用方法都不正确,没理解临界区如何运作的。
      

  6.   

    为了测试问题,我特意把其他的代码都屏蔽掉了,只保留了下面这些:
    FThreadCriticalSection.Enter();
      try
        try
             if FrmEntry.FIsDebugMode then StrToInt('2');
        except
             On E:Exception do FExcutionException:=E;
        end;
      finally
        FThreadCriticalSection.Leave();
      end;假如是临界区操作问题,我觉得应该也只会导致TList的数据不正确,不至于报异常对吧,因为我只是修改TList中的值,没有其他操作。
      

  7.   

    当然会异常,举个例子,当然不一定是你的情况,A线程修改list的最后一个item之前,发生了线程切换到B线程删除了list中任意一个item,必须异常。
      

  8.   

    还请帮忙看一下,这里的try except看似捕获到了异常,但是线程还是有出现不正常,为了检验线程是按正常的时钟在执行的,我做了个测试。procedure TPlcDataReaderThread.CallPlcMethodsHelper;
    begin
      FThreadCriticalSection.Enter();
      try
        try
            StrToInt('ABC');  //触发异常
        except
          On E:Exception do FExcutionException:=E;
        end;
      finally
        FThreadCriticalSection.Leave();
      end;
    end;procedure TPlcDataReaderThread.Execute;
    begin
      while (not Terminated) do
      begin
        if FDoExecution then
        begin
            FLastExcutionTime:=now();  //发生异常后没有执行
            FExcutionException:=nil;
            //
            CallPlcMethodsHelper();        FDoExecution:=false;
        end;
      end;
    end;  //以下代码每4秒执行一次
      with FPlcDataReaderThread do
      begin
          if (not Terminated) then
          begin
             if FIsDebugMode then Log('FLastExcutionTime:'+DateTimeToStr(FLastExcutionTime));
             if FExcutionException<>nil then Application.HandleException(FExcutionException);
             FDoExecution:=true;
          end;      if Suspended or Terminated then Log('PLC reading thread stopped');  //发生异常后没有执行
      end;
    有异常时候的日志:
    FLastExcutionTime     SystemDate
    2014/3/23 11:27:00 2014-03-23 11:27:12.687
    2014/3/23 11:27:00 2014-03-23 11:27:08.620
    2014/3/23 11:27:00 2014-03-23 11:27:04.557注释掉异常代码后的日志:
    2014/3/23 11:09:57 2014-03-23 11:09:59.220
    2014/3/23 11:09:53 2014-03-23 11:09:57.103
    2014/3/23 11:09:48 2014-03-23 11:09:53.047从上面的结果我们看出异常是捕获到了,但是线程貌似就停掉了,但是线程的状态却没有变化,比如:Suspended和 Terminated属性都没有变化。
      

  9.   

    Exception在try except结束后就会被释放了,所以你在try except块外是不能访问。
      

  10.   

    pathletboy,谢谢你这么热心的帮忙,可能的话真想请你吃个饭。