DELPHI盒子
!实时搜索: 盒子论坛 | 注册用户 | 修改信息 | 退出
检举帖 | 全文检索 | 关闭广告 | 捐赠
技术论坛
 用户名
 密  码
自动登陆(30天有效)
忘了密码
≡技术区≡
DELPHI技术
lazarus/fpc/Free Pascal
移动应用开发
Web应用开发
数据库专区
报表专区
网络通讯
开源项目
论坛精华贴
≡发布区≡
发布代码
发布控件
文档资料
经典工具
≡事务区≡
网站意见
盒子之家
招聘应聘
信息交换
论坛信息
最新加入: a12315
今日帖子: 49
在线用户: 10
导航: 论坛 -> DELPHI技术 斑竹:liumazi,sephil  
作者:
男 zhouying (zy) ★☆☆☆☆ -
盒子活跃会员
2021/11/20 23:01:57
标题:
是窗口没有关闭的问题么? 浏览:1914
加入我的收藏
楼主: 最近再关闭程序时候,总会出现如下的错误,是因为我关闭了未打开的窗体么?还是说其他的问题,大哥们帮忙看下呢。
Exception class EOSError with message 'System Error,Code 1400'无效的窗口句柄。
----------------------------------------------
-
作者:
男 oyefer (oyefer) ★☆☆☆☆ -
盒子活跃会员
2021/11/20 23:32:53
1楼: 你这问得太笼统了。
你的程序有没有用到多线程,线程中有没有操作窗体控件?
----------------------------------------------
-
作者:
男 emailx45 (emailx45) ▲▲▲▲△ -
普通会员
2021/11/21 3:13:25
2楼: You can verify some like this:

... 
function ImInThreadWaitOrRunning(xxxxThread : TThread type ):boolean;
begin
  result :=  xxxxThread.Running or Waiting....
end


... "Close" event:

onClose ...
begin
  //
  CanClose := NOT(ImInThreadWaitOrRunning(zzzThread));
  //
end;


....
procedure TForm1.BtnThreadTerminateClick(Sender: TObject);
begin
  if Assigned(MyThreadAnonimous) then
  begin
    MyThreadAnonimous.Terminate;
    //
    MyThreadAnonimous := nil;
    //
    // Self.Close; // ok
    // Application.Terminate; // ok
    // Halt; // AV...
  end;
end;
----------------------------------------------
The higher the degree, the greater the respect given to the humblest!RAD 11.3
作者:
男 jackalan (nVicen) ★☆☆☆☆ -
盒子活跃会员
2021/11/21 9:35:49
3楼: 一看就是线程还没释放
----------------------------------------------
简单做人,认真做事。
作者:
男 bluestorm8 (bluestorm) ▲▲△△△ -
普通会员
2021/11/21 10:05:08
4楼: 【Delphi】实现程序关闭时自动先关闭线程【20210125更新】
https://blog.csdn.net/BlueStorm/article/details/110921061

所需做的就是把uGenericThread.pas单元加入到项目,把你的线程继承自该单元的TGenericThread而不是TThread,这样就可以了
----------------------------------------------
-
作者:
男 ybj316 (ybj) ★☆☆☆☆ -
盒子活跃会员
2021/11/21 18:57:44
5楼: 有D7 的版本吗?  @bluestorm8
----------------------------------------------
-
作者:
男 keymark (嬲) ▲▲▲△△ -
普通会员
2021/11/21 20:01:56
6楼: @bluestorm8 (bluestorm)
按此在新窗口浏览图片
学习了,拿走了。
  ThreadList: TList<TThread>;
一个泛型。
----------------------------------------------
[alias]  co = clone --recurse-submodules  up = submodule update --init --recursiveupd = pullinfo = statusrest = reset --hard懒鬼提速https://www.cctry.com/>http://qalculate.github.io/downloads.htmlhttps://www.cctry.com/
作者:
男 bluestorm8 (bluestorm) ▲▲△△△ -
普通会员
2021/11/21 20:09:58
6楼: unit uGenericThread;  //实现主Form关闭时,程序自动先关闭线程, 然后才关闭主Form
          //此单元中,线程数>0时重载了主Form的OnCloseQuery事件
interface

uses Classes, SysUtils, Types, Windows, Forms, Messages, SyncObjs;

type
  TGenericThread = class(TThread)
  private
    class procedure NewCloseQuery(Sender: TObject; var CanClose: Boolean);
  public
    constructor Create(aCreateSuspended: Boolean = False);
    destructor Destroy; override;
  end;

implementation

var
  CritSect: TCriticalSection;
  MainFormClosing: Boolean;
  ThreadList: TList;
  OriginalCloseQuery: procedure(Sender: TObject; var CanClose: Boolean) of Object;

class procedure TGenericThread.NewCloseQuery(Sender: TObject; var CanClose: Boolean);
var
  I: Integer;
  Thread: TThread;
begin
  CritSect.Enter;
    if ThreadList.Count <> 0 then
    begin
      CanClose := False; //还有线程没释放的情况下,暂时不关闭主Form
      MainFormClosing := True;
      for i := 0 to ThreadList.Count-1 do
      begin
        Thread := ThreadList[I];
        Thread.Terminate;
      end;
    end;
  CritSect.Leave;
end;

//==========

constructor TGenericThread.Create(aCreateSuspended: Boolean);
begin
  CritSect.Enter;
    if ThreadList.Count = 0 then
    begin   //一旦有线程创建,主Form的OnCloseQuery就使用这里定义的FormCloseQuery
      OriginalCloseQuery := Application.MainForm.OnCloseQuery;
      Application.MainForm.OnCloseQuery := NewCloseQuery;
    end;
    ThreadList.Add(Self);
  CritSect.Leave;

  inherited;
  FreeOnTerminate := True;
end;

destructor TGenericThread.Destroy;
begin
  //在FreeOnTerminate := True的情况下,Destroy的执行实际上是在线程里面进行的
  CritSect.Enter;
    ThreadList.Remove(Self); //对ThreadList的操作需要在临界保护区里完成
    if (ThreadList.Count = 0) then
    begin //一旦线程释放完毕,主Form的OnCloseQuery恢复使用原来定义的FormCloseQuery
      Application.MainForm.OnCloseQuery := OriginalCloseQuery;
      if MainFormClosing then   //线程已经释放完毕,发消息关闭主Form
        PostMessage(Application.MainForm.Handle, WM_CLOSE, 0, 0);
    end;
  CritSect.Leave;

  inherited;
end;

initialization
  MainFormClosing := False;
  ThreadList := TList.Create;
  CritSect   := TCriticalSection.Create;

finalization
  ThreadList.Free;
  CritSect.Free;

end.
----------------------------------------------
-
作者:
男 ybj316 (ybj) ★☆☆☆☆ -
盒子活跃会员
2021/11/21 21:10:29
7楼: D7 下验证了 ,暂无效果。 有时间自己看看代码了! 再次感谢!@bluestorm8
----------------------------------------------
-
作者:
男 bluestorm8 (bluestorm) ▲▲△△△ -
普通会员
2021/11/21 22:10:23
8楼: //你的线程要继承于TGenericThread,而不是TThread,示例如下:

unit Unit1; //测试用

interface

uses
  Windows, Messages, SysUtils, Variants,
  Classes, Graphics, Controls, Forms, Dialogs,
  uGenericThread;

type
//改为TTestThread = class(TThread)会报内存泄漏,证明线程没有自动释放
  TTestThread = class(TGenericThread)
  public
    procedure Execute; override;
  end;

  TForm1 = class(TForm)
    procedure FormShow(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TTestThread.Execute;
var
  I: Integer;
begin
  Sleep(Random(1000));
  while not Terminated do
  begin
    for I := 1 to 100000000 do
    begin
      Sqrt(I);
    end;
    sleep(50);
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ReportMemoryLeaksOnShutDown := True;
  Position := poScreenCenter;
end;

procedure TForm1.FormShow(Sender: TObject);
begin
  TTestThread.Create;
  TTestThread.Create;
  TTestThread.Create;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
 //
end;

end.
----------------------------------------------
-
作者:
男 tiez (骑牛夜旅) ★☆☆☆☆ -
普通会员
2021/11/22 15:41:13
9楼: 6楼的东西做得挺有用的,
      for i := 0 to ThreadList.Count-1 do
      begin
        Thread := ThreadList[I];
        Thread.Terminate;
      end;
1。这个改成downto更好一点,按栈的形式处理虽然不能完全解决万一有线程会建立线程对象又被其它再参照或线程内建锁的问题,但后面的先关总是能解决多数较简单的问题,放心一点儿。
2。不要用FreeOnTerminate := True;把Thread.Terminate;用三行代替,
Thread.Terminate;
Thread.waitfor;
Thread.Free;
这个还是一个一个关,再配合一下上面的倒序更安全。毕竟我们不知道Thread.Free;会释放些啥。

如果做的程序有通讯、视频等回调处理的最好还是手写线程处理。特别是调用了外部SDK内部有线程操作的情况下,又不明确SDK内线程回收细节的,手写比较明确,可能还要判断application.terminating,或另加标志位。
----------------------------------------------
-
作者:
男 dbyoung (dbyoung) ★☆☆☆☆ -
普通会员
2021/11/22 16:15:42
10楼: 这个 bug 很容易重现:
  关闭 Form2 后,激活窗体回到 Form1,
  Form1 上第一个激活控件,刚好不可用(变灰或不可视),就会报这样的错误。
  
解决方法(两种方法任一都可以):
  1、找到 Form1 上第一个激活的控件,修改状态。
  2、xxx.SetFocus,修改成 Winapi.Windows.SetFocus(xxx.Handle);即使不可用,也不会报错。
----------------------------------------------
武汉天气不好
作者:
男 bluestorm8 (bluestorm) ▲▲△△△ -
普通会员
2021/11/22 16:22:49
10楼: for循环处在临界区里面,因此对ThreadList的操作是安全的,不需要改为downto

也不需要用Thread.waitfor,因为程序直到最后一个线程对象被Destroy后,才会发消息通知主form关闭。
----------------------------------------------
-
作者:
男 bluestorm8 (bluestorm) ▲▲△△△ -
普通会员
2021/11/22 16:37:18
11楼: 使用TGenericThread这个类最大的好处,就是让关闭线程的代码不和其他代码混在一起,提高了代码的隔离度,而且这些代码完全可以不做任何修改而重用。
----------------------------------------------
-
作者:
男 tiez (骑牛夜旅) ★☆☆☆☆ -
普通会员
2021/11/24 10:50:53
12楼: @bluestorm8谢谢您的回复,可能我们的思路有点不同。
我是这样理解的,你做了一个List存线程表,对所有线程生存期处理时依赖一个锁保证只会有一个线程处理生存问题。不知道这是不是你的想法。
但其实线程在处理生存期时的锁与线程内容是无法先验性的建立关系的。就像在例子TTestThread.Execute里的操作与锁是无关的。所以我提了一个建议用downto,这并不能完全解决问题,就是为开发时提供了一个潜意识“我要保证倒序释放时对象和锁的参照性不出错,程序就不太可能在关闭时报错。我在自行操作释放时也要注意到这一点”。有了这个想法你写复杂程序时排除了一些可能,大脑就不再需要把各种可能放在一起思考,会轻松一些。
关于Thread.waitfor的问题我也是这么考虑的,我很怕使用FreeOnTerminate,因为不清楚使用了这个之后通过Thread.Terminate关闭线程时线程的内容到底什么时候释放(Thread.Terminate调用后就置了个标志,它是不能阻塞后续操作直到线程清理完毕的)。Thread.waitfor之后保证你主线程在Thread.Terminate之后会阻塞等你把上一个清理完毕后再进行下一个清理。
其实就是说我开发时使用的思路是在脑中建立一个线程堆栈,为自己做个预设的限制,以在较复杂情况下简化一点思路。这样出现不明原因的程序退出偶然报错我会对我的线程管理保有信心。
可能你的思路是用了别的方法比如对象管理和锁操作有自己的思路以保证不出现类似的情况,当你单独共享上述程序时我们可能无法立即学习到你的思路和做法。所以我提了上面两个建议,说明了一下倒序销毁和强行保证销毁的阻塞序列可以解决一定的问题。
说下我自己做线程的体会吧:线程是危险的,尤其是在释放的时候。
----------------------------------------------
-
作者:
男 tiez (骑牛夜旅) ★☆☆☆☆ -
普通会员
2021/11/24 11:54:26
13楼: 另外给大家共享我自己写的一个工具包源码吧。里面可能对大家最有用的是BFCmn.SysUtils里的一些函数,特别是TBFBytesHelper = record Helper for TBytes,这个虽然内容不多,但对写通讯的人来说挺有用的。
BFCmn.Containers单元里有自己写的锁和锁的托管器。因为我写的一些程序可能建立操作大量的线程和锁,所以考虑了用托管器来管一下。    这里面还有一个TBFRingQueue<T> = class这个简单的东西是我做视频处理的时候用的,如果是做这方面的都知道帧处理要考虑CPU处理不过来的情况,当时就搞了环状队列,原始版本很复杂功能很多,那时也没有泛型,多做了几次写熟了就把功能扒出去了(因为做核心小工具时不用做太复杂),现在就剩下这个光杆子环了。
BFCmn.Classes.pas这个单元里面的东西功能挺强也挺复杂,想用起来要看挺久代码。
BFCmn.DragDrop.pas里面是专门做的界面控件拖放用的,目的是为了把简单的拖放时的代码变复杂:),当然是有应用目的的,这个对大家用处应该不大。
BFCmn.Config.pas这是为ini做增强的,可以把配置写成record直接与ini文件交互,原来的几行的一些操作也改成一行就能做到了。有点儿用。
BFCmn.Debug.pas里面是基于codesite做的一些扩展。就是你写几行代码,再装个codesite在部署的机器上,日志就能按日自动分割记录了。专门为怕单个日志文件太大的情况使用的。最终效果和codesite界面控件的按日分割啥的可能只有美学上的差异:)

希望大家把我的的代码扒出去用,能用啥用啥。这样简单理解后用就不用问我啥了。另外我是一直追新的,环境用D11是最好的,D10.4.2应该也没问题,D10更前的版本可能会有一两个函数改过了所以和老的不通,

放度盘了
链接:https://pan.baidu.com/s/1Wak1qOLWmEBttzZcdPOxkA 
提取码:2ccc
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2021/11/24 12:09:53
13楼: 我用线程的体会:
1. 线程不危险,非常简单。
2. 代码多了,线程多了,线程用到的其它地方创建的对象多了,尤其是有用到接口,那就比较麻烦一点,也仅仅是麻烦而已,不是危险。


3. 代码复杂了都很麻烦。因为可能有些互相纠缠的地方没注意到。在 WINDOWS 底下我面对这种情况,用了一个绝招:程序退出时,直接杀死自己,而不是逐个对象去释放。这样,它连弹异常的机会都没有。
----------------------------------------------
-
作者:
男 wiseinfo (wisienfo) ★☆☆☆☆ -
普通会员
2021/11/24 12:12:27
14楼: 用QWorker可以学多线程
----------------------------------------------
-
作者:
男 bluestorm8 (bluestorm) ▲▲△△△ -
普通会员
2021/11/24 12:18:24
14楼: @tiez 我从来没觉得释放线程有什么危险,你对它了解多一些后,也就那么回事,如此而已。
你所说的锁,其实就是我把对ThreadList的操作都放在临界区里面进行,保证ThreadList在任何时间内不会有超过一个线程对它同时进行操作,因为TList不支持多线程同时操作

我这么多年一直使用FreeOnTerminate := True;,没遇到过什么问题。
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2021/11/24 12:22:17
15楼: 楼上,TThreadList 的用法,本来就是 Lock 干活,然后 UnLock 的啊。

这个 Lock 就是加锁啊。
----------------------------------------------
-
作者:
男 bluestorm8 (bluestorm) ▲▲△△△ -
普通会员
2021/11/24 12:30:35
16楼: @pcplayer 我当然知道临界区需要锁来实现,我强调的是通过临界区实现在任何时间内最多只有一个线程操作ThreadList。
----------------------------------------------
-
作者:
男 tiez (骑牛夜旅) ★☆☆☆☆ -
普通会员
2021/11/24 20:43:41
17楼: @pcplayer 真不危险吗?不要忽悠小朋友:)
  我上面发的包里面BFCmn.Containers单元里有个锁托管器,目的是对锁进行托管(这样说话是最近刚学的,我女儿给我发了个如何说废话的教学视频)。就是用的TThreadList<T>这里的T是接口类型的,利用接口的参照数对锁释放进行自动处理的。那里面有重复加锁地方,但没有BUG哦。

  好吧,我还是讲一下这个线程使用中的问题吧。坛子上都是老程序员,好多对这里面的东东熟得很,所以我说再重新说下废话吧。讲下线程相关的几个基本概念。懂的别看了,没新东西!

一、线程是并行运行的
  就是说,线程启动很简单,但后面你做任何操作的时候这个线程都在运行。包括你试图关闭他的时候。所以你考虑线程管理的时候不能只从外部考虑,要考虑线程内部。

二、做线程的关注点在哪
  开发多线程应用是为了利用多线程并发的特性,关注点最好集中在做线程时对我们操作的限制上面。明确了限制(危险)有哪些,按固定的方法做事。带着枷锁跳舞,这就是精彩的地方。造成限制一般是线程内操作到的对象、锁、事件等等,应该在决定启动一个线程前就考虑好对它们操作的限制。启动一个线程让它直接跑,跑完就算。那很简单,那我搞个匿名线程跑就得了。

三、做多线程时涉及到东西的特点
  线程本身的特点没啥好说的,不是说没内容,而是说相关内容都要吃透。
  线程运行不是孤立的,它往往是带着一大堆东西跑的。这些东西如果与外部相关,你还想打包到线程内去管理生存周期,那情况就要比线程操作外部的东西麻烦一些,要更小心的操作。比如对象是不是要用锁,那锁由谁管,放里放外不同都会有些不同。
  线程是单次线程还是循环线程,如果循环线程是采用脉冲触发还是用事件触发,不同模式也要考虑对上面对象的。
  线程采用什么样的锁,这东西也有很大不同,用临界对象还是用Tmonitor还是别的,它们在生存周期的不同阶段访问有啥限制。

@bluestorm,pcplayer是老程序员,他说的意思就是TThreadList本身就是List加了锁功能的,如果你只是简单的想搞个队列管线程又想对队列本身加锁的话就不用再自己做锁了。就这么个简单的意思。

  这里注意一下,TThreadList<T>这个泛型看名字很容易误以为是专门用来放线程的,其实本意并不是,首先这个泛型里面的是T而不是T:TThread就知道这是个通用列表,所以它也被放在通用容器单元内。这一点可以通过TObjectList<T: class>的情况做对照,这东西就是加了个锁,在简单的快速应用时可以用用,就是名字取得不符合delphi的一般习惯,这点不太好,改成TImpLockedList就好了(就是隐式锁定列表的意思)。
  TThreadList<T>,这东西只是锁定列表操作,和线程自身无关。现在你做了一个带锁列表管理线程,那起到什么作用呢?你写了一个程序,在多个线程中可能对此列表内的线程进行创建销毁动作,那你才可能用到这个锁。在你设想的应用条件下,由于是主线程预定最终操作,所以实际你是在防备其它子线程进行此列表内线程创建销毁动作可能产生并发问题。
   
  总结一下吧:线程销毁时的报错,一般出在线程访问到的对象上,我们只能通过合理安排线程内外操作防止出错(即从内外同时配合)。一个简单的小工具可以在限定范围内起一点简化或自动化的效果,但要预设好限制。

   说了这么多,肯定有人要问:妈蛋,尽说销毁报错,咋不说运行呢?
   其实做多线程,正经的报错有一半是销毁时出现,而且比运行报错更难意识到。
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2021/11/24 22:42:40
18楼: 楼上说的都没错。

线程这玩意,该注意的注意到了,基本不会出什么问题。

网上很多讲编程的文章,都把线程讲得很可怕,搞得我刚开始编程,很长时间尽量避免用线程。不用,就不熟,不懂。

后来实在避免不了,开始用线程,一开始也是各种问题,但实际碰几次问题,自己再总结一下经验,发现其实真的不难,很简单。所以,不要说得很吓人,把小朋友吓到不敢用线程了。

比如,作为新人,我犯过的错误,线程循环中间,满足条件就干活,不满足条件就不干活。结果线程一通狂跑,把 CPU 占满。后来发现这个问题,我给没干活的地方加上 Sleep(100),啥活不干,CPU 也没占用了。

比如,线程没活干,我就让它等消息,用 TEvent 阻塞住不让它跑等消息。结果,退出程序时,消息没来,线程死在那里,想退出都不行。后来为了避免这种麻烦,我干脆让线程空循环,而不是阻塞死等。这样至少程序退出时线程也能退出。

然后就是一个比较基本的东西:对任何数据(包括对象)的访问,都要加锁。当然,这个数据只有这个线程自己用,就无需加。如果是几个线程都要用 --- 线程往往要从其它线程或者主线程获得数据,处理完的数据往往要丢给其它线程,总之几个线程都要用的数据,加锁。搞定加锁,基本上也就没问题了。

所以后来我写过的程序,一个程序里面几十个线程在跑,是很常见的,也不会搞得焦头烂额纠缠半天。

当然,作为新手,就会几个线程纠缠到一起搞不清楚。这个时候,连单步跟踪都不好搞,最好的就是用那个 LOG 工具, Eur..什么来着?
----------------------------------------------
-
作者:
男 bluestorm8 (bluestorm) ▲▲△△△ -
普通会员
2021/11/25 9:06:02
19楼: @zhouying 楼主提完问题后就失踪了,剩下几个人在这里傻吱吱,有种莫名其妙的感觉
----------------------------------------------
-
作者:
男 dbyoung (dbyoung) ★☆☆☆☆ -
普通会员
2021/11/25 11:13:52
20楼: @pcplayer,关于多线程说说我的使用感受。不对之处,还望大侠指正。

1、看着觉得 Delphi 关于线程的封装已经很好用了,但多线程、匿名线程到底有什么区别,
  新手、老人,有几人能弄清楚明白?
  不自己亲手写个3、5个多线程、匿名线程的例子,不跑个10、20遍,是弄不明白到底有什么区别的。
  别人说的、网上说的,和自己的理解是两码事。

2、本意是想用多线程来加速数据处理的,加了个锁,效率至少降低了一个数量级。
  所以我尽量避免使用锁。多个线程多个列表。
  全部处理完了,在主线程中再做处理(加个等待提示框,没什么。稳定压倒一切)。
  或者直接提交到数据库(临时表也OK)。但在多线程中使用数据库,又有一些需要注意的地方(小坑)。

3、100次测试,只要出了一次 BUG,测试就算通不过。
  多线程偶尔冒出的错误,真的很难查。关键是这个“偶尔”狠要人命。
  而且,即使用 EurekaLog、DebugView、日志,等等手段找到了错误,
  如果不懂得原理,你依然不知道如何修改才算正确。
  举个例子:
    你通过参数,传递 TList/TStringList 给多线程,然后在多线程中,
    往 TList/TStringList 中添加数据(没有多个线程一起操作这个 TList/TStringList)。
    这种做法,一般情况下不会出问题。99% 情况下都是正确的。但是“偶尔”还是会出问题的。

4、业务需求多了、改了,程序也随着复杂了。
  原先的框架不可能考虑到未来种种,所以最后肯定是补丁套补丁。
  多线程和 EXE、DLL、框架、消息、回调、数据库、UI 绘制、数据对接,
  等等放在一起时,真的就是一部天书了(企业的管理系统还是很庞大的)。
  再举个例子:
    你在多线程中,某个函数中创建一个局部变量 ado:TAdoQuery ;忘记了释放。
    在一般的 EXE 中,这没有什么大问题。局部变量,编译器会自己处理释放。
    但在一个 DLL 中,这个忘记释放的局部变量,就会导致程序奔溃。
    而且 Delphi 的错误提示,针对多线程,基本没什么帮助作用。EurekaLog 也不行。

5、看过最变态的代码,一个只需要几行 SQL 查询代码就能完成的事情,
  偏偏调用了一个数据库封装类。而且这个类中还使用了泛型列表来保存数据。
  据“老人”说,他们也是从网上 COPY 而来。
  这种代码,在多线程中使用,能放心吗?经过严格测试了吗?
  写出这种封装的高高高手,肯定是不知道别人到底在什么情况下使用这个类的。
  一旦出了BUG,这种代码调试起来,简直是要人命。

综上种种,我还是尽量避免使用多线程。
必须使用多线程时,也尽量保持其独立性。只完成某个特定功能。也避免使用锁。
能用定时器的,绝不用多线程。

我是不建议新人使用多线程的。
因为新手们往往都是 COPY 代码直接来用的,也不明白其中的原理。

只有自己亲手写的代码,测试妹妹那里通过的,才算真正成熟的代码。
即使这样,随着环境、需求的变化,这样的代码依然会出问题。和死/亡、税收一样。
----------------------------------------------
武汉天气不好
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2021/11/25 11:36:08
21楼: 20楼:
1. 同意。看书不如自己动手做了看结果。动手做了再改,磨几次,就基本上能理解代码背后的机制。光看书看资料是学不会的。

2. 加锁影响效率,但是,不加锁可能会出问题。这种情况下,我选择加。程序稳定不出问题是第一的。效率第二。我的实际经验,我们的代码,对效率的要求没那么高,加了锁,也没多消耗多少 CPU 或者内存。

3. 【你通过参数,传递 TList/TStringList 给多线程,然后在多线程中,
    往 TList/TStringList 中添加数据】 -- 如多传递数据给这个 TList 的代码不是被后面往里面添加数据的代码的相同线程执行,那就是多个线程在操作同一个数据,那就应该加锁。一定要记住:主线程,也是一个线程。可能你所谓的加数据是通过 Button.OnClick 来加,没认为它是线程?
----------------------------------------------
-
信息
登陆以后才能回复
Copyright © 2CCC.Com 盒子论坛 v3.0.1 版权所有 页面执行117.1875毫秒 RSS