没想到Delphi在线程安全方法作的会这么差。(200分)

  • 主题发起人 主题发起人 程云
  • 开始时间 开始时间

程云

Unregistered / Unconfirmed
GUEST, unregistred user!
没想到Delphi在线程安全方法作的会这么差。
我现设一列表对象 AList: TStrings
我有多个线程来来维护这个列表,
TOnCap = procedure(S: String) of object;
TCYThread = class(TThread)
private
FOnCap: TOnCap;
protected
procedure Execute;
override;
public
property OnCap: TOnCap read FOnCap write FOnCap;
end;

是通过TCYThread线程的property OnCap事件中写程序来,这时这个事件需要带个参数,
procedure OnCap(S: String),
要完成的任务就是先在这个列表(AList: TStrings)中查找是否有这个字符串,
如没有,就添加上,
可假设同时有两个以上带有同一个字符串的线程去访问这个列表(AList: TStrings),
它们很可能会在发现这里没有这个值而同时为这个列表(AList: TStrings)加入这个值,
结果造成列表列项重复。
查资料得知,VCL在线程安全方面是个大缺陷,
关有两种方法可解决,
第一种:使用同步函数Synchronize来进行排队,写法如下:
procedure Execute
begin
Synchronize(OnCap(sTemp))
end;
可是Synchronize这个破函数不支持有参数的过程。
第二种:PostMessage向那个对象发消息,使用Windows的消息队列来完成,
这个好象也不好,因为,有可能我要写很多各种各样的参数,
这根本解决不了问题。
嗨!为何傻乎乎的Borland会出这么多错。
有没有好办法解决?
 
Derive a class from TStrings to store and manipulate a list of strings. TStrings contains abstract methods and should not be directly instantiated.
 
保持线程方法多的是,按照你上述情况,可以考虑使用临界区,上班时间,没有时间多说,再有问题,可以在qq上说
 
就是单独让一段代码排队使用,,
 
1.如楼上所说的:排队,多线程变成了单线程:)
2.适当延时
 
就算Synchronize不支持带参数的函数,你也可以变通下,使用线程内的全局变量
编写一个不带参数的的过程来供Synchronize调用,很简单嘛。做程序要灵活的变通一下
 
procedure TplayFrm.proStartcolor(var msg: TMESSAGE);
begin
///开始处理刷色
if ShowtextSongword.Mode='男:' then
FontColor:=clblue;
if ShowtextSongword.Mode='女:'then
FontColor:=clred;
if ShowtextSongword.Mode='合:' then
FontColor:=clLime;
InitializeCriticalsection(cs);//这里初始化临界区变量
if OneLine then
begin
OneLineLabel.ColorFront.Normal:=FontColor;
OneLineThread:=TwaitThread.Create(true);
OneLineThread.Lineindex:=oneline;
OneLineThread.Priority:=tpTimeCritical;
OneLineThread.SongRecord:=ShowtextSongword;
OneLineThread.Resume;
end
else
begin
TwoLineLabel.ColorFront.Normal:=Fontcolor;
TwoLineThread:=TwaitThread.Create(true);
TwoLineThread.Lineindex:=oneline;
TwoLineThread.Priority:=tpTimeCritical;
TwoLineThread.SongRecord:=ShowtextSongword;

TwoLineThread.Resume;
end;
oneLine:=not oneLine;
if readSongLine then
postmessage(playfrm.Handle,MY_Startrun,0,0);
end;

procedure Twaitthread.Execute;
var
i:integer;
begin
Entercriticalsection(CS);//进入临界区,
playfrm.ReShowTimer.Enabled:=false;
freeonterminate:=true;
LabelLength:=0;
ModewordLength:=playFrm.OneLineLabel.Canvas.TextWidth(SongRecord.Mode);
for i:=0 to high(SongRecord.waittime)do
begin
first:=gettickcount;
size:=playertop.playFrm.OneLineLabel.Canvas.TextWidth(SongRecord.TextLength);
temp:=SongRecord.waittime;
AddLength:=size/(temp/80);
if i=high(SongRecord.TextLength)div 2then
begin
postmessage(playfrm.Handle,MY_readfile,0,0);
end;
while gettickcount-first<tempdo
begin
if Terminated then
exit;
last:=gettickcount;
LabelLength:=LabelLength+AddLength;
playertop.playFrm.showcolor(lineindex,labelLength,ModeWordLength);
if gettickcount-last>80 then
sleep(1)else
sleep(80-(gettickcount-last)+1);
end;
end;
LabelLength:=playertop.playFrm.OneLineLabel.Canvas.TextWidth(SongRecord.SongContext);
playertop.playFrm.showcolor(lineindex,labelLength,ModeWordLength);
LabelLength:=0;
postmessage(playfrm.Handle,MY_Startrun,0,0);
if playertop.FileEnd then
playfrm.ClearTimer.Enabled:=true;
if (playertop.ShowTextSongWord.FirstTime-(GetTickCount-playertop.StartTime)>6000) then
playfrm.ReShowTimer.Enabled:=true;
playertop.OneLine:=not playertop.OneLine;
leavecriticalsection(CS);//退出临界区,,,
end;
不好意思,懒得去过滤了,慢慢看吧,,
 
不需要这么复杂吧
把参数做成线程类的属性,在线程create的时候把参数传递进来
就可以在 Execute中使用Synchronize调用一个不带参数的过程
这样就可以实现同步了;
 
最简单的是:
增加一个
procedure TCYThread::DoCap;
begin
if Assigned(FOnCap) then
FOnCap(参数);
end;
需要的地方,synthonize(DoCap);
 
to: 程云
自己对线程理解不行别怪的Delphi
建议找一本Windows线程方面的书看一看。
不要总看Delphi的书
 
告诉你我对付 Synchronize 的办法
TOnCap = procedure(S: String) of object;
TCYThread = class(TThread)
private
FOnCap: TOnCap;
FS: string;
// here !!!
protected
procedure Execute;
override;
procedure _MyOnCap;
public
property OnCap: TOnCap read FOnCap write FOnCap;
procedure MyOnCap(s: string);
end;

procedure TCYThread.MyOnCap(s: string);
begin
FS := s;
Syntronize(_MyOnCap);
end;

procedure TCYThread._MyOnCap;
begin
OnCap(FS);
end;

procedure TCYThread.Execute;
begin
MyOnCap(sTemp);
end;
 
楼上的都说得很对了,其它我的方法也是差不多,只不过用临界区应该比用Syntronize要快点,
因为Syntronize是由主线程调度的,要是Syntronize用多了,多线程就失去了意义了,
 
做线程也用不着完全依赖vcl吧!
去看看这些api吧!
createevent
CreateMutex
CreateSemaphore
InitializeCriticalSection
 
多谢大家,我已解决了
^_^
呵呵,Delphi老完偏手,得十刻小心了,
它不会给你出堂堂正正之师的
只是Synchronize的使用,就和不用线程一样,
看来这个方法也是不成。
to mygod!:
老兄,你的程序我不太懂,
InitializeCriticalsection(cs);
中的cs是作什么用的?
又如何使用它的也看不明白
 
CS这是个临界区
InitializeCriticalsection初始化
EnterCriticalSection 进入临界区 //写在线程内
加入你要互斥的代码
LeaveCriticalSection 离开临界区//写在线程内
DeleteCriticalSection 删除临界区
 
TCriticalSection in SyncObjs
cs:TCriticalSection;
TOnCap = procedure(S: String) of object
begin
cs.Enter;
.......
....
cs.Leave
end;
 
//摘自中文开发在线 
一.基本的方法如下:
  1.从Tthread类派生一个新类。(创建TSortThread类)
  2.定义新类的Create方法。
  3.定义新类的Execute方法,并在Execute方法中插入线程运行时执行的代码。
  4.引用类方法创建实例。
  二.例子的详细代码及说明:
  首先,新建一个单元,保存为mysort.pas.在此单元中,我们创建了一个TSortThread类,它从TThread类中继承而来,所以当我们在程序中创建这个类的一个实例时,也就是创建了一个新的线程。
  接着,在该类中定义一个Sort方法,用来对数组进行排序,同时TSortThread类超越了TThread类的构造方法Create和Execute,在execute方法中,调用了对数组进行排序的Sort方法。具体代码如下:
  unitmysort;
  interface
  usesClasses;//TThread类在Classes中被定义。
  type
  PSortArray=TSortArray;
TSortArray=array.[0..MaxIntdivSize?
Of(Integer)-1]ofInteger;
  {此处定义了TsortThread类}
  TSortThread=class(TThread)
  Private
  {在TSortThread类中定义了如下几个私有变元}
  FSortArray:PSortArray;
  FSize:Integer;
  FA,FB,FI,FJ:Integer;
  Protected
  {类TSortThread超越了类Tthread的Execute方法}
  procedure Execute;override;
  {类TsortThread添加了一个Sort方法}
  procedure Sort(varA:arrayofInteger);
  public
  {类TSortThread超越了类Tthread的构造方法}
  constructorCreate(varSortArray:arrayofInteger);
  end;

  implementation
  constructorTSortThread.Create(varSortArray:arrayofInteger);
  begin

  FSortArray:=@SortArray;
  FSize:=High(SortArray)-Low(SortArray)+1;
  FreeOn Terminate:=True;
  inheritedCreate(False);
  end;

  {当线程开始时,Execute方法将被调用。}
  procedure TSortThread.Execu?
te;
  begin

  Sort(Slice(FSortArray,FSize));
  end;

  {下面实现了冒泡法排序}
  procedure TSortThread.Sort(varA:arrayofInteger);
  var
  I,J,T:Integer;
  begin

  for I:=High(A)downto Low(A)do

  for J:=Low(A)to High(A)-1do

  if A[J]>A[J+1] then

  begin

  T:=A[J];
  A[J]:=A[J+1];
  A[J+1]:=T;
  if Terminated then
Exit;
  end;

  end;

  end
  最后,在用户应用程序的implementation处加入usesmysort,在执行排序的地方加入TQuickSortthread.Create(SortArray),其中SortArray为一实际的数组。这样就可以用线程实现排序,在排序过程中,用户不必等到排序结束就可以执行其它操作。这种用多线程实时响应用户操作方法在涉及大量数据处理的应用程序中显得尤为重要。
 
后退
顶部