线程中数组控件的问题,代码如下,你运行几次后就出现'Access violation at ...'错误,请各位高手帮我,先谢谢了(100分)

  • 主题发起人 主题发起人 redted3
  • 开始时间 开始时间
R

redted3

Unregistered / Unconfirmed
GUEST, unregistred user!
线程中数组控件的问题,代码如下,你运行几次后就出现'Access violation at ...'错误
,请各位高手帮我,先谢谢了
Form1是主form
procedure TForm1.Button1Click(Sender: TObject);
begin
Form2:=TForm2.Create(nil);
Try
Form2.SHowModal;
FInally
Form2.free;
Form2:=nil;
end;
end;

form2测试线程
uses Thrdu;
{$R *.dfm}
var
MyThread:TTestThread;
procedure TForm2.BtnCloseClick(Sender: TObject);
begin
close;
end;

procedure TForm2.BtnTestThreadClick(Sender: TObject);
begin
if ThreadList.count<=0 then
begin
try
MyThread:=TTestThread.Create(False);
ThreadList.Add(self);
Except
MyThread.Terminate;
end;
end;
MyThread.Priority:=tpNormal;
MyThread.CtrlEdit[0]:=Edit1 As TEdit;
MyThread.CtrlEdit[1]:=Edit2 As TEdit;
end;

procedure TForm2.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
if ThreadList.Count>0 then
begin
MyThread.terminate;
end;
end;

procedure TForm2.FormCreate(Sender: TObject);
begin
ThreadList:=TList.Create;
end;

end.

线程单元Thrdu
unit Thrdu;
interface
uses
Classes,SysUtils,StdCtrls;
type
TTestThread = class(TThread)
private
Answer:Integer;
public
CtrlEdit:Array[0..1] of TEdit;
protected
procedure CtrlEditEnabled;
procedure Execute;
override;
end;

var
ThreadList:TList;
//因为该线程是通用的,要在几个form中调用,所以我就定义这里了
implementation
procedure TTestThread. CtrlEditEnabled;
begin
(CtrlEdit[0] As TEdit).Enabled:=False;
(CtrlEdit[1] As TEdit).Enabled:=False;
end;

procedure TTestThread.Execute;
var
i:integer;
begin
try
FreeOnTerminate:=True;
for i:=1 to 200000000do
begin
if Terminated then
break;
Inc(Answer,Round(Abs(Sin(Sqrt(I)))));
CtrlEdit[0].Text:=IntToStr(Answer);
end;
Finally
ThreadList.Pack;
ThreadList.Count:=0;
//(CtrlEdit[0] As TEdit).Enabled:=False;
//(CtrlEdit[1] As TEdit).Enabled:=False;
Synchronize(CtrlEditEnabled);
//错误就在这里和上面两行,如果我去掉这行
//就没有问题
end;
end;

end.
 
应该这样:
procedure TForm2.BtnTestThreadClick(Sender: TObject);
begin
if ThreadList.count<=0 then
begin
MyThread:=TTestThread.Create(true);
MyThread.CtrlEdit[0]:=Edit1;
MyThread.CtrlEdit[1]:=Edit2;
MyThread.resume;
MyThread.Terminate;
end;
end;
 
这样子能行么?
 
原以为大富翁论坛上高手众多,现在很感失望!我这个问题看来是无解了
 
>>ThreadList:=TList.Create;
请注意:TLIST不是线程安全的,多个线程可能同时访问它并造成错误。请改用:
ThreadList:=TThreadList.Create
每次使用加锁,如下:
with ThreadList.LockListdo
try
...
finally
ThreadList.UnlockList;
end;
(如果是ADD或REMOVE则可以不用,TThreadList会自动加锁)
 
我觉得是这样的:线程terminate下来总是先绕到下面form的finally中即:
form2.free....后再到线程的Execute方法的Finally中执行那段代码,因此我认为错误原因
在于form2已经free了所至。

 
你的代码问题多多,所有VCL都是线程不安全的,TEDIT也一样,你在给EDIT控件赋值时,
应该使用Synchronize同步(CtrlEdit[0].Text:=IntToStr(Answer);这句没同步)。
楼上说的也有道理,你应该先等待线程结束才FREE FORM2,因为线程中用了FORM2的EDIT。
 
谢谢了,但还是会出错的,错误就在线程的Finally中那个地方。
把form2.free;
form2:=nil;
拿掉,就一点问题没有了,但这和我的目标不符:占用资源最小;代码精炼。哪位有相同爱好的高手有没有更好的解法?
 
你应该先等待线程结束才FREE FORM2,请注意MyThread.terminate线程是不会马上结束的。
简单点你可以改为
procedure TForm2.FormClose(Sender: TObject;
var Action: TCloseAction);
var
I: Integer;
begin
if ThreadList.Count>0 then
begin
for I:=ThreadList.Count-1do
wnto 0do
TMyThread(ThreadList.Items).Terminate;//结束
for I:=ThreadList.Count-1do
wnto 0do
TMyThread(ThreadList.Items).WaitFor;//等待
end;
end;
 
唉,由于ThreadList不安全,这段代码仍可能会报错。
 
谢谢了,可是有错啊,错在waitfor,说是"system error""无效句柄",麻烦帮人帮到底
 
唉,我说了,由于ThreadList不安全,这段代码仍可能会报错。
如果有两个线程在ThreadList中,当第一个在WAITFOR时,第二个已经结束并FREE了,
然后ThreadList毫不知情,照样取下一个ITEM,可这个ITEM已经结束并FREE掉了,于是
要出错。
我改成下面的了(出错几率会降低,但绝对还有可能出错):
var
I: Integer;
begin
if ThreadList.Count>0 then
begin
for I:=ThreadList.Count-1do
wnto 0do
TMyThread(ThreadList.Items).Terminate;//结束
while ThreadList.Count>0do
//等待
Application.ProcessMessages;
for I:=0 to 5000do
//再多等待一会,如果线程能在此之前结束,则不会报错,否则,照错不误
Application.ProcessMessages;
end;
end;

建议你还是用安全的TThreadList,从根本上解决问题。我对你的代码感觉非常不自在。
 
多谢老兄帮忙!虽然我也知道TList是线程不安全的,可根踪调试了好多次错误都和它无关啊,
所以你刚开始说我还非常不信,现在改过是是好些了,但新的问题又出来了:
线程的finally部分
Finally
Synchronize(CtrlEditEnabled);
with ThreadList.LockListdo
//执行到这一句就不住下走了,所以threadlist就没有清空
begin
try
Pack;
Count:=0;
finally
ThreadList.UnlockList;
end;
end;
end;
由于threadlist没有清空,所以线程执行完一次就无法再执行下一次,你可以试把2000000改为2000试一下
下面是改过后的
procedure TForm2.BtnTestThreadClick(Sender: TObject);
begin
if ThreadList.LockList.count<=0 then
begin
try
MyThread:=TTestThread.Create(False);
ThreadList.Add(self);
Except
MyThread.Terminate;
end;
end;
MyThread.Priority:=tpNormal;
MyThread.CtrlEdit[0]:=Edit1 As TEdit;
MyThread.CtrlEdit[1]:=Edit2 As TEdit;
end;
procedure TForm2.FormClose(Sender: TObject;
var Action: TCloseAction);
var
I: Integer;
begin
if ThreadList.lockList.Count>0 then
begin
MyThread.terminate;
end;
end;
 
>>with ThreadList.LockListdo
//执行到这一句就不住下走了,所以threadlist就没有清空
这说明ThreadList被某个线程锁住了并且一直没有放锁。
可能是下面这段改过锁住的:
procedure TForm2.BtnTestThreadClick(Sender: TObject);
begin
with ThreadList.LockListdo
try
if Count<=0 then
try
MyThread:=TTestThread.Create(False);
Add(self);
//你为什么ADD Self? 我还以为你要Add MyThread呢。
//TThreadList.Add会自动加锁并解锁,这里已经加锁,不需要它了
Except
MyThread.Terminate;
end;
finally
ThreadList.UnlockList;
end;
...
另外,搞不懂你为何要用PACK(PACK是将为NIL的项删除,CLEAR则调用Count:=0),
用CLEAR就行了:
with ThreadList.LockListdo
try
Clear;
finally
ThreadList.UnlockList;
end;
 
太谢谢了!这两个地方我理解上有问题,你这么一解说我就清楚了。再次道谢![:)][:)]
 
后退
顶部