多线程程序,界面frozen,什么原因?我该如何处理? ( 积分: 100 )

  • 主题发起人 主题发起人 leobug
  • 开始时间 开始时间
L

leobug

Unregistered / Unconfirmed
GUEST, unregistred user!
一个简单的程序,用TThread启动一个线程,执行过程中经常出现界面不响应的情况。
界面上已经没有什么程序了,我该如何处理?
线程代码如下:
procedure T_Test_Thread.Execute;
begin
while not Terminateddo
begin
MyClass.Do_sth();
sleep(1000);
end;
end;

线程启动如下:
T_Test_Thread.Resume;
中间会有多次的Resume和suspend
madExcepion 的bug report 如下
process id : $aec
allocated memory : 5.22 MB
executable : Project1.exe
exec. date/time : 2007-03-18 01:19
madExcept version : 2.7i
exception message : The application seems to be frozen.
main thread ($e44):
7c92eb94 +00 ntdll.dll KiFastSystemCallRet
77d1ca62 +f6 user32.dll PeekMessageA
00483823 +13 Project1.exe Forms TApplication.ProcessMessage
004838b2 +0a Project1.exe Forms TApplication.HandleMessage
00483ad2 +96 Project1.exe Forms TApplication.Run
004880cf +3b Project1.exe Project1 16 +3 initialization
disassembling:
7c92eb94 public KiFastSystemCallRet: ;
function entry point
7c92eb94 > ret
 
补充一下,界面冻结时,启动的线程还在正常处理。只是无法切换到窗口界面
我也不是很确认是否和多线程有关。但是不启动线程时,还没有发现冻结的情况。
 
线程代码中不要用访问主程序中的界面元元素, 如button.caption. edit.text 等
如果要访问, 可委托给主线程来访问, 或采用 sycronize 那个函数据来实现
 
procedure T_Test_Thread.Execute;
begin
while not Terminateddo
begin
MyClass.Do_sth();
sleep(1000);
end;
end;
在此处加上sleep(1000),会让该线程挂住,如果是要停一段时间,建议用event这种核心对象,在线程创建时用CreateEvent创建一个事件hEvent。然后线程代码改为
procedure T_Test_Thread.Execute;
begin
FreeOnterminated := true;
while not Terminateddo
begin

MyClass.Do_sth();
WaitForSingleObject(hEvent, 1000);
//前面第一个参数是你创建的事件对象,后面的是你停留的时间。这样就不会用到sleep,线程执行到该句时会等待,而不是忙等,占用CPU时间,当该hEvent事件变成激活对象(即1000ms时间到后),就会让该线程继续运行。你查下CreateEvent。这个函数创建的事件有参数,会影响事件对象是手动,还是自动触发。
end;
end;
 
如果你代码确实需要用线程,比如后台进行大量的计算,就应用线程,最好不要用 sycronize来同步,因为这个其实是交由主线程处理了,你的界面一样会被frozen。如果只是操作界面,建议用计时器好些(也不一定,要看你操作的界面是否是VCL 安全的)
 
计时器不需要管VCL是否安全。我指的是线程中。以前我一个项目,设计失误,在线程中操作界面。麻烦死了。我就改成计时器了。呵。
 
群里也要线程讨论高手,喜欢delphi就来qq群:23981160
 
感谢各位帮忙。问题还是没有解决:
1.我在启动线程中并没有访问任何界面元素。
2.当界面停止响应时,线程的程序执行正常,在sleep之后的程序仍然可以正常运行,但是界面却始终无法恢复。
3.并不是每次都会导致界面冻结,出现的很随机,只是概率比较高。
4.我换成了WaitForSingleObject(hEvent, 1000);
的方式,依然存在问题。
 
...你能帖部分代码上来不???不知道你是怎么写的。
 
MyClass.Do_sth();
这段代码详细贴上来,并且说明MyClass是在哪里声明的,谁管理它的生存期,有无在MyClass类的设计中访问了全局对象,或者引用使用了界面上甚至非界面上的对象。
 
不是不能贴,是代码比较多,现在没在手边,晚上回去贴。
大概结构是这样的。
TForm1,是界面
TMyClass和TMyThread在同一个.pas中定义。
TMyThread中定义了一个TMyClass的实例
在Form中定义了一个MyClass,一个MyThread,并在Create方法中实例化了

procedure TFrom1.Create();
begin
myClass:=TMyClass.create;
myThread:=TMyThread.create;
myThread.myClass := myClass;
end;
在Form1的方法中会设置myClass的属性,启动、暂停线程。在线程运行中也会改变myClass的属性。(会不会是这儿的问题?同时在操作myClass的内容?但是似乎出问题的时候都没有界面操作,也就是没有在form中操作啊)
myclass和myThread都没有操作全局和界面的内容。
 
对使用MyClass的地方要进行同步,可以使用阻塞或者信号量,当然最简单的方法是使用临界区
在主线程定义一个临界区
在操作MyClass的时候Enter临界区
操作完毕后Leave临界区
 
这个很明显是MyClass.Do_sth()中包含了对VCL控件的操作(也可能是Do_sth()调用的函数中包含有),因些界面frozen。VCL只能在主线程中安全运行,其他线程要调用它就要使用Synchronize或者其他间接的措施。

Synchronize example
This example shows how to call a button's click method in a thread-safe manner:
procedure TMyThread.PushTheButton;
begin
Button1.Click();
end;

procedure TMyThread.Execute;
begin
...
Synchronize(PushTheButton);
...
end;

------------------------------
通过事件来更新界面也可以。
TMyThread = class(TThread)
protected
procedure Execute;
override;
public
UpDataUi:procedure(Work_ID:integer;Msg:string)of object;

...
end;
procedure TMyThread.Execute;
begin
...
if assigned( UpDataUi) then
UpDataUi(self);

...
end;
-----
假设线程是MyThread,窗体叫Form1,在Form1中调用。
MyThread.UpDataUi :=Form1.Button1Click;
 
代码确实太多太乱,不好贴。
几位的答复我还是有点问题:
to muhx:是否对myClass进行同步确实会影响到线程安全,但是会造成界面冻结么?
to qqjm: 在我的TMyClass和TMyThread中都没有任何对界面的操作。实际上是一个简单的外挂程序,线程是调度MyClass对游戏进程进行处理。 判断条件是在线程启动前赋值。所以只有Form对MyClass和MyThread的操作,没有任何反向操作
另:在我的类中用到了 TStringList,帮助中说这个类不是线程安全的,但我还是那个问题,线程安全会导致界面冻结么?
其实问题解决不解决无所谓,主要是想弄明白是怎么回事。谢谢各位:)[:)]
另:我的类中用到的方法主要有ReadProcessMemory,WriteProcessMemory,PostMessage,AnsiContainsText
主要对象有:TObjectList,TStringList
再没别的可疑的了
 
对了,我的程序中还有几个Timer,都是比较简单的,结构类似访问TMyClass, ReadProcessMemory,PostMessage.
会不会是Timer中的操作和线程的操作之间死锁了?
 
是这样的,你的线程的Execute方法访问MyClass,这段代码是在独立空间执行的;如果同时这个方法以外的方法也在同一时刻访问MyClass(不管是读取属性还是调用其方法),都是在另外一个独立空间内执行的!所以两者必然会产生冲突,必须要做同步。最简单的同步方式就是用Synchronize方法,这个在你新建一个Thread类的时候就会有段英文做了说明。另外还可以用临界区,事件,互斥对象等方式同步,你可以参考一下网上有关的代码。记住一点,只要线程外部访问线程操作的对象,或者反过来说,线程操作的对象在线程之外也会被访问的话,就必须100%做同步,否则会出现任何未知的情况!
 
http://www.2ccc.com/article.asp?articleid=3740
这里是我写过的一个用Observer模式来进行线程同步的代码,不知道对你有帮助没。呵呵。代码说明在我的博客上。
 
"线程操作的对象在线程之外也会被访问的话,就必须100%做同步,否则会出现任何未知的情况!"
就是说,这里的“任何未知的情况”也包括界面的冻结?
另:看了您的博客,很受教,我理解与界面交互的关键就是不要在界面中等待线程结果,而是用线程调用界面,调用时要使用syncronize?
 
后退
顶部