delphi VCL显示问题 分线程与主线程的同步
我知道Delphi中的VCL组件大多都是线程不安全的.现在遇到问题了,我在写一个程序的时候,创建线程是用creattheard,而不是TTheard类,其分网络线程需要读...
我知道Delphi中的VCL组件大多都是线程不安全的.现在遇到问题了,我在写一个程序的时候,创建线程是用creattheard,而不是TTheard类,其分网络线程需要读取网络数据,然后更新Treeview,前面运行无错,每次运行更新VC时,DELPHI就报错误,类似什么内存不能为read的错误.我怀疑是VCL的安全问题,原则上分线程是不能访问VCL的.可是我的分线程必须更新TREEVIW,而且在creatheard建的线程里面,我一使用Synchronize就会报错,请问该如何实现如主线程的同步?
帖出部分源码:
procedure SocketWorkThread(ns :TSocket);stdcall;
const
buflen=1024*8;
var
recvbuf :array[0..buflen -1] of Char;
rtn :Integer;
rs :string;
error :string;
ld:integer;
S1,dirn,filn,dd:string ;
sendbuf :array[0..buflen +90000] of Char;
sendlen:integer;
strr:string;
strs:string;
ds:integer;
i,j,k,m,n,l,o:integer; //循环变量
ListItem,listitems:TListItem;
send_str:string;
contral:string;
is_server:boolean;
contral_array,recv_array,bufs_array: array of string;
recv_str,contral_str,temp_str:string;
t,z,test:integer;
filemanage_hostnode, filemanage_disknode,filemanage_dicnode,regedit_keynode: TTreeNode;
send_socket:integer;
begin_down:boolean;
label
line3;
begin
try
while true do
(中间recv读取网络封包的源码省略,所有读取的内容全部存到了bufs_array数组中)
//----------根据控制指令执行相应操作
case strtoint(contral) of
0001:
0002:
... ...
0004: //添加目录列表
begin
filemanage_nodeclick.DeleteChildren;
for k:=0 to high(bufs_array) do
begin
filemanage_dicnode:=form1.treeview1.Items.AddChild(filemanage_nodeclick,bufs_array[k]);
New(pp);
pp^.treeview1_socket:= ns;
pp^.ii := 2;
pp^.tag :=PMyData(filemanage_nodeclick.Data)^.tag+bufs_array[k]+'\';
filemanage_dicnode.Data := pp;
filemanage_dicnode.ImageIndex := 5;
filemanage_dicnode.SelectedIndex :=6;
end;
filemanage_nodeclick.Expand(true);
node_lock:=false;
form1.timeout.Enabled:=false;
//form1.Memo1.Lines.Add('目录数量是'+inttostr(high(bufs_array)+1));
end;
0005://添加文件列表
begin
form1.listview2.Items.Clear;
for k:=0 to high(bufs_array) do
begin
form1.listview2.Items.Add.caption:=bufs_array[k] ;
form1.listview2.Items[k].imageindex:=image_index(bufs_array[k]);
form1.listview2.items[k].SubItems.Add(PMyData(form1.TreeView1.Selected.Data)^.tag+bufs_array[k]);
end;
//form1.Memo1.Lines.Add('文件数量是:'+inttostr(high(bufs_array)+1));
end;
... ....
其间添加文件列表,和添加目录列表会出错
请问该如何使用Synchronize???
有劳大侠指点一二!
哦,那也许问题不在VCL上,问题是内存地址出错?
是不是指针的问题? 展开
帖出部分源码:
procedure SocketWorkThread(ns :TSocket);stdcall;
const
buflen=1024*8;
var
recvbuf :array[0..buflen -1] of Char;
rtn :Integer;
rs :string;
error :string;
ld:integer;
S1,dirn,filn,dd:string ;
sendbuf :array[0..buflen +90000] of Char;
sendlen:integer;
strr:string;
strs:string;
ds:integer;
i,j,k,m,n,l,o:integer; //循环变量
ListItem,listitems:TListItem;
send_str:string;
contral:string;
is_server:boolean;
contral_array,recv_array,bufs_array: array of string;
recv_str,contral_str,temp_str:string;
t,z,test:integer;
filemanage_hostnode, filemanage_disknode,filemanage_dicnode,regedit_keynode: TTreeNode;
send_socket:integer;
begin_down:boolean;
label
line3;
begin
try
while true do
(中间recv读取网络封包的源码省略,所有读取的内容全部存到了bufs_array数组中)
//----------根据控制指令执行相应操作
case strtoint(contral) of
0001:
0002:
... ...
0004: //添加目录列表
begin
filemanage_nodeclick.DeleteChildren;
for k:=0 to high(bufs_array) do
begin
filemanage_dicnode:=form1.treeview1.Items.AddChild(filemanage_nodeclick,bufs_array[k]);
New(pp);
pp^.treeview1_socket:= ns;
pp^.ii := 2;
pp^.tag :=PMyData(filemanage_nodeclick.Data)^.tag+bufs_array[k]+'\';
filemanage_dicnode.Data := pp;
filemanage_dicnode.ImageIndex := 5;
filemanage_dicnode.SelectedIndex :=6;
end;
filemanage_nodeclick.Expand(true);
node_lock:=false;
form1.timeout.Enabled:=false;
//form1.Memo1.Lines.Add('目录数量是'+inttostr(high(bufs_array)+1));
end;
0005://添加文件列表
begin
form1.listview2.Items.Clear;
for k:=0 to high(bufs_array) do
begin
form1.listview2.Items.Add.caption:=bufs_array[k] ;
form1.listview2.Items[k].imageindex:=image_index(bufs_array[k]);
form1.listview2.items[k].SubItems.Add(PMyData(form1.TreeView1.Selected.Data)^.tag+bufs_array[k]);
end;
//form1.Memo1.Lines.Add('文件数量是:'+inttostr(high(bufs_array)+1));
end;
... ....
其间添加文件列表,和添加目录列表会出错
请问该如何使用Synchronize???
有劳大侠指点一二!
哦,那也许问题不在VCL上,问题是内存地址出错?
是不是指针的问题? 展开
2个回答
展开全部
虽然很多资料上说和VCL同步最好用Synchronize,但我测试的结果是不用也没有影响,帮助上也是只是说Synchronize causes the call specified by Method to be executed using the main thread, thereby avoiding multi-thread conflicts.
至少不会出现“内存不能为read的错误”的错误,可能代码哪里写错了。
我想说的只是Synchronize和VCL同步没关系。
如果需要同步那就得同步。Synchronize只是同步的一种方法(更准确的说是提供一种在主线程中队列执行的方法),你可以使用任何其他的方法。
关键是FOnProgress做的是什么,如果仅仅是操作VCLSynchronize反而多此一举。操作VCL并不会冲突,不相信的话可以试一下。
至少不会出现“内存不能为read的错误”的错误,可能代码哪里写错了。
我想说的只是Synchronize和VCL同步没关系。
如果需要同步那就得同步。Synchronize只是同步的一种方法(更准确的说是提供一种在主线程中队列执行的方法),你可以使用任何其他的方法。
关键是FOnProgress做的是什么,如果仅仅是操作VCLSynchronize反而多此一举。操作VCL并不会冲突,不相信的话可以试一下。
展开全部
不用没影响 是因为你线程少了.多线程.
Synchronize(这里是一个过程,没有参数的过程);
你要对vcl有什么操作,就写在这个过程里面.
因为delphi里面线程的Synchronize这个方法就是把线程中定义的这个方法的指针传给主线程,然后主线程调用这个方法来执行vcl操作,所以这样才保证了安全.
我举个例子吧.贴点代码,我用的TThread继承的类.
TDataStread = class(TThread)
private
FSock:TClientSocket;
FFile:TFileStream;
FTotalSize,FStartPos:Integer;
FHandle:THandle;
FProgress:TOnDownProgress;
pStr:TWinSocketStream;
procedure setDone;//这个就是我上面说的那个没有参数的过程.
protected
procedure execute;override;
public
constructor Create(bSuspend:Boolean;aSock:TClientSocket;aFile:TFileStream;var aStart,aTotal:Integer;ahandle:THandle;aProgress:TOnDownProgress); overload;
destructor Destroy; override;
end;
创建线程
aThread:=TDataStread.Create(True,FDataSock,FFileStream,FStartPos,FTotalSize,Self.handle,FOnProgress);
aThread.Resume;
aThread.OnTerminate:=freeThread;
FCtrlSock.Active:=False;
然后就是同步的地方
procedure TDataStread.execute;
var
p:array[0..24*1024 -1] of Char;
nOne:Integer;
begin
pStr:=TWinSocketStream.Create(FSock.Socket,6000);
while (not terminated)and(FSock.Active) do
begin
if pStr.WaitForData(6000) then
begin
try
if Boolean(pStr) then
nOne:=pStr.Read(p,24*1024);
//
if Boolean(FFile) then
begin
FFile.Seek(FStartPos,soFromBeginning);
Inc(FStartPos,FFile.Write(p,nOne));
end;
//
Synchronize(setDone);
if FStartPos=FTotalSize then
begin
SendMessage(FHandle,wm_fileDone,FStartPos,0);
Terminate;
end;
except
FreeAndNil(pStr);
end;
end
else
begin
SendMessage(FHandle,wm_fileDone,FStartPos,0);
Terminate;
end;
end;
end;
procedure TDataStread.setDone;
begin
if Assigned(FProgress) then
FProgress(Self,FStartPos,FTotalSize);
end;
Synchronize(这里是一个过程,没有参数的过程);
你要对vcl有什么操作,就写在这个过程里面.
因为delphi里面线程的Synchronize这个方法就是把线程中定义的这个方法的指针传给主线程,然后主线程调用这个方法来执行vcl操作,所以这样才保证了安全.
我举个例子吧.贴点代码,我用的TThread继承的类.
TDataStread = class(TThread)
private
FSock:TClientSocket;
FFile:TFileStream;
FTotalSize,FStartPos:Integer;
FHandle:THandle;
FProgress:TOnDownProgress;
pStr:TWinSocketStream;
procedure setDone;//这个就是我上面说的那个没有参数的过程.
protected
procedure execute;override;
public
constructor Create(bSuspend:Boolean;aSock:TClientSocket;aFile:TFileStream;var aStart,aTotal:Integer;ahandle:THandle;aProgress:TOnDownProgress); overload;
destructor Destroy; override;
end;
创建线程
aThread:=TDataStread.Create(True,FDataSock,FFileStream,FStartPos,FTotalSize,Self.handle,FOnProgress);
aThread.Resume;
aThread.OnTerminate:=freeThread;
FCtrlSock.Active:=False;
然后就是同步的地方
procedure TDataStread.execute;
var
p:array[0..24*1024 -1] of Char;
nOne:Integer;
begin
pStr:=TWinSocketStream.Create(FSock.Socket,6000);
while (not terminated)and(FSock.Active) do
begin
if pStr.WaitForData(6000) then
begin
try
if Boolean(pStr) then
nOne:=pStr.Read(p,24*1024);
//
if Boolean(FFile) then
begin
FFile.Seek(FStartPos,soFromBeginning);
Inc(FStartPos,FFile.Write(p,nOne));
end;
//
Synchronize(setDone);
if FStartPos=FTotalSize then
begin
SendMessage(FHandle,wm_fileDone,FStartPos,0);
Terminate;
end;
except
FreeAndNil(pStr);
end;
end
else
begin
SendMessage(FHandle,wm_fileDone,FStartPos,0);
Terminate;
end;
end;
end;
procedure TDataStread.setDone;
begin
if Assigned(FProgress) then
FProgress(Self,FStartPos,FTotalSize);
end;
已赞过
已踩过<
评论
收起
你对这个回答的评价是?
推荐律师服务:
若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询