是不是主线程和线程冲突?100分!!(100分)

  • 主题发起人 主题发起人 zflegend
  • 开始时间 开始时间
Z

zflegend

Unregistered / Unconfirmed
GUEST, unregistred user!
前几天做了一个全文检索的小程序,其功能是在检索的同时将得到的文件名称分类加入到一个treeview控件中,并在检索过程中可以点击treeview控件将其展开。程序采用多线程,在线程Execute方法中调用主线程的过程进行检索。如果等到检索完毕再将treeview控件展开是没有问题的,但是在检索过程中将treeview控件展开就会出错,发生结点插入混乱及一些结点得不到子结点等错误。
最让我绝望的是错误仅发生在treeview控件的立滚动条出现后,只要立滚动条不出现,怎么点击也不会有问题。这个问题我始终无法解决,只好求助各位大虾了!
由于全文检索源程序比较大,还需要用来搜索的文本,各位看起来会比较麻烦,于是用我原来程序的框架编了个小程序贴出来,出现的问题也和我原来的程序相同,请大家帮忙看看。
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ComCtrls, StdCtrls;
type
TForm1 = class(TForm)
StartButton: TButton;
TreeView1: TTreeView;
procedure FormCreate(Sender: TObject);
procedure JobStart;
procedure Cycbegin
;
//循环开始
procedure AddNode(iint:integer);
//添加结点
procedure StartButtonClick(Sender: TObject);
private
{ Private declarations }
public
end;

TMyThread = class(TThread)
private
{ Private declarations }
protected
procedure Execute;
override;
public
constructor Create;
end;

var
Form1: TForm1;
implementation
{$R *.DFM}
constructor TMyThread.Create;
begin
inherited Create(false);
end;

procedure TMyThread.Execute;
begin
Form1.JobStart;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
firstnode,dirnode : TTreeNode;
begin
firstnode:=TreeView1.Items.GetFirstNode;
dirnode := TreeView1.Items.AddChild(firstnode, '根结点');
dirnode.HasChildren := true;
end;

procedure TForm1.JobStart;
var
topnode:TTreeNode;
begin
topnode:=TreeView1.TopItem;
topnode.DeleteChildren;
//开始前清空TreeView
Cycbegin
;
end;

procedure TForm1.Cycbegin
;
var
i:integer;
begin
for i:=1 to 400do
begin
AddNode(i);
sleep(50);
end;
end;

procedure TForm1.AddNode(iint:integer);
var
dirnode,topnode,searchnode,leafnode : TTreeNode;
begin
//先将1、2、3做为一级子结点加入
topnode:=TreeView1.TopItem;
if (iint=1) or (iint=2) or (iint=3) then
dirnode := TreeView1.Items.AddChild(topnode, inttostr(iint));
//将十位数与父结点相等的二位数加入
if length(inttostr(iint))=2 then
begin
searchnode:=topnode.getFirstChild;
while searchnode<>nildo
begin
if pos(searchnode.Text,inttostr(iint))=1 then
dirnode := TreeView1.Items.AddChild(searchnode, inttostr(iint));
searchnode:=searchnode.getNextSibling;
end;
end;

//将百、十位与父结点相等的三位数加入
if length(inttostr(iint))=3 then
begin
dirnode:=topnode.getFirstChild;
while dirnode<>nildo
begin
searchnode:=dirnode.getFirstChild;
while searchnode<>nildo
begin
if pos(searchnode.Text,inttostr(iint))=1 then
leafnode := TreeView1.Items.AddChild(searchnode, inttostr(iint));
searchnode:=searchnode.getNextSibling;
end;
dirnode:=dirnode.getNextSibling;
end;
end;
end;

procedure TForm1.StartButtonClick(Sender: TObject);
var
MyThread_1:TMyThread;
begin
MyThread_1:=TMyThread.Create;
end;

end.
 
如果是treeview控件不支持多线程而出现问题的话,请各位指出其他能实现这种功能(即在检索过程中能够点击展开treeview控件,因为程序有这个要求)的方法,谢谢!!
 
去看 Delphi 的帮助:TThread.Synchronize
 
用过Synchronize方法,但是这样程序就变成单线程了,树结点都点不开!
 
用过Synchronize方法,但是这样程序就变成单线程了,树结点都点不开
----------------------------------------------------------------
你锁定的过程级别过大,应该这样:
procedure TMyThread.Execute;
begin
Synchronize(清除节点过程);
for i := 0 to 1000do
begin
Synchronize(单独加入一个节点的过程);
Sleep(10);

end;
end;
 
把检索的程序都放在线程里!
 
to Xeen:
谢谢你的热心回答!
按照你的方法修改了程序,运行和原来的还是一样,在treeview控件展开到出现滚动条的时候出现错误!修改后的程序如下:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ComCtrls, StdCtrls;
type
TForm1 = class(TForm)
StartButton: TButton;
TreeView1: TTreeView;
procedure FormCreate(Sender: TObject);

procedure StartButtonClick(Sender: TObject);
private
{ Private declarations }
public
end;

TMyThread = class(TThread)
private
{ Private declarations }
protected
procedure Execute;
override;
procedure DeleteNode;
procedure AddNode;
public
constructor Create;
end;

var
Form1: TForm1;
i:integer;
implementation
{$R *.DFM}
constructor TMyThread.Create;
begin
inherited Create(false);
end;

procedure TMyThread.Execute;
begin
Synchronize(DeleteNode);
for i:=1 to 400do
begin
Synchronize(AddNode);
Sleep(50);
end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
firstnode,dirnode : TTreeNode;
begin
firstnode:=TreeView1.Items.GetFirstNode;
dirnode := TreeView1.Items.AddChild(firstnode, '根结点');
dirnode.HasChildren := true;
end;

procedure TMyThread.DeleteNode;
var
topnode:TTreeNode;
begin
topnode:=Form1.TreeView1.TopItem;
topnode.DeleteChildren;
//开始前清空TreeView
end;

procedure TMyThread.AddNode;
var
dirnode,topnode,searchnode,leafnode : TTreeNode;
begin
//先将1、2、3做为一级子结点加入
topnode:=Form1.TreeView1.TopItem;
if (i=1) or (i=2) or (i=3) then
dirnode := Form1.TreeView1.Items.AddChild(topnode, inttostr(i));
//将十位数与父结点相等的二位数加入
if length(inttostr(i))=2 then
begin
searchnode:=topnode.getFirstChild;
while searchnode<>nildo
begin
if pos(searchnode.Text,inttostr(i))=1 then
dirnode := Form1.TreeView1.Items.AddChild(searchnode, inttostr(i));
searchnode:=searchnode.getNextSibling;
end;
end;

//将百、十位与父结点相等的三位数加入
if length(inttostr(i))=3 then
begin
dirnode:=topnode.getFirstChild;
while dirnode<>nildo
begin
searchnode:=dirnode.getFirstChild;
while searchnode<>nildo
begin
if pos(searchnode.Text,inttostr(i))=1 then
leafnode := Form1.TreeView1.Items.AddChild(searchnode, inttostr(i));
searchnode:=searchnode.getNextSibling;
end;
dirnode:=dirnode.getNextSibling;
end;
end;
end;

procedure TForm1.StartButtonClick(Sender: TObject);
var
MyThread_1:TMyThread;
begin
MyThread_1:=TMyThread.Create;
end;

end.
 
topnode:=Form1.TreeView1.TopItem;
----------------------------------------
这句话有问题,TopItem是最深的可见节点,会随着用户的选择而改变。
我不知道你的意思是不是:
topnode:=Form1.TreeView1.Items[0];
 
不要试图用可视vcl控件来作线程中需要访问数据的容器,因为可视vcl在屏幕刷新时通常会访问其存储的一大批数据,从而与线程中访问相同数据部分产生共享冲突。而且由于vcl很多都是delphi系统本身自带的或者是第三方提供的,无法或不便进行源码级修改,所以很难做到在最小范围内同步不同线程的操作。要避免线程冲突,只有在更大范围内进行同步,通常造成两个线程变成串行执行,造成比单线程更低的执行效率。
 
感谢xeen,问题解决了!
也感谢Another_eYes和jmlwz的参与!
 

Similar threads

Q
回复
4
查看
356
穿越沦陷的爱
穿
回复
3
查看
351
迷惘的人
B
回复
22
查看
265
Tophi
T
后退
顶部