我正在使用Delphi XE2,我的应用程序用于通知twitter / rss中的新记录。在我的应用程序中,我使用2个线程从twitter和amp;中获取一些数据。 rss每秒。
以下是代码:
类型部分:
TWarframeEvent=record
GUID: String;
EventType: Byte; // 0 = unknown; 1 = alert; 2 = invasion; 3 = infestation
Planet: String;
Mission: String;
EventDate: TDateTime;
Time: Integer;
RewardCredits: LongWord;
RewardOther: String;
RewardOtherAmount: Integer;
Notified: Boolean;
ItemIndex: Integer;
Hidden: Boolean;
end;
TWarframeNotifyEvent=record
NotifyTimeLeft: LongWord;
ID: Integer;
FlashOnTaskbar: Boolean;
PlaySound: Boolean;
Volume: Integer;
TrayPopupBalloon: Boolean;
end;
TWarframeEventList=record
WarframeEvent: Array of TWarframeEvent;
WarframeEventCount: Integer;
NotifyEvent: TWarframeNotifyEvent;
end;
TUpdateFromTwitterThread=class(TThread)
TwitterURL: String;
Procedure Execute; override;
end;
TUpdateFromRSSThread=class(TThread)
RSS_URL: String;
Procedure Execute; override;
end;
var section(module)
WarframeEventList: TWarframeEventList;
实施部分:
procedure TForm1.TimerUpdateEventsTimer(Sender: TObject);
begin
UpdateFromTwitterThread:=TUpdateFromTwitterThread.Create(True);
UpdateFromTwitterThread.TwitterURL:=form2.EditAlertsURL.Text;
UpdateFromTwitterThread.Start;
UpdateFromRSSThread:=TUpdateFromRSSThread.Create(True);
UpdateFromRSSThread.RSS_URL:=form2.EditInvansionsURL.Text;
UpdateFromRSSThread.Start;
end;
procedure TUpdateFromTwitterThread.Execute;
var
HTTPClient: TIdHTTP;
IOHandler: TIdSSLIOHandlerSocketOpenSSL;
S, S2: String;
i, l: Integer;
NewAlertDate: TDateTime;
ErrorLogFile: TextFile;
begin
HTTPClient:=TIdHTTP.Create(nil);
IOHandler:=TIdSSLIOHandlerSocketOpenSSL.Create(nil);
try
try
HTTPClient.IOHandler:=IOHandler;
HTTPClient.HandleRedirects:=True;
S:=HTTPClient.Get(self.TwitterURL);
except
Assign(ErrorLogFile, AppPath+'Error.log');
if not(FileExists(AppPath+'Error.log')) then
Rewrite(ErrorLogFile);
Append(ErrorLogFile);
Writeln(ErrorLogFile, DateTimeToStr(Now)+' '+LS_ErrorConnection+' '+self.TwitterURL+'; Error code: '+IntToStr(HTTPClient.ResponseCode));
Close(ErrorLogFile);
self.Terminate;
HTTPClient.Free;
IOHandler.Free;
end;
finally
HTTPClient.Free;
IOHandler.Free;
end;
if Application.Terminated or self.Terminated then exit;
S:=copy(S, pos('<b>WarframeAlerts</b></span>', S), Length(S)-pos('<b>WarframeAlerts</b></span>', S));
while pos('tweet-timestamp js-permalink js-nav', S)>0 do begin
S:=copy(S, pos('tweet-timestamp js-permalink js-nav', S)+35, Length(S)-pos('tweet-timestamp js-permalink js-nav', S)-35);
S2:=copy(S, pos('data-time="', S)+11, Length(S)-pos('data-time="', S)-11);
NewAlertDate:=StrToInt(copy(S2, 1, pos('"', S2)-1));
S2:=copy(S, pos('<p class="js-tweet-text tweet-text">', S)+36, pos('</p>', S)-pos('<p class="js-tweet-text tweet-text">', S)-36);
for i:= 0 to WarframeEventList.WarframeEventCount-1 do
if (WarframeEventList.WarframeEvent[i].EventDate=NewAlertDate) and (WarframeEventList.WarframeEvent[i].Planet=copy(S2, 1, pos(')', S2))) then
NewAlertDate:=0;
if NewAlertDate=0 then continue;
Inc(WarframeEventList.WarframeEventCount);
SetLength(WarframeEventList.WarframeEvent, WarframeEventList.WarframeEventCount);
for i:= 0 to WarframeEventList.WarframeEventCount-2 do begin
if WarframeEventList.WarframeEvent[i].EventDate>NewAlertDate then begin
for l:=WarframeEventList.WarframeEventCount-1 downto i+1 do
WarframeEventList.WarframeEvent[l]:=WarframeEventList.WarframeEvent[l-1];
Break;
end;
end;
if i<=WarframeEventList.NotifyEvent.ID then Inc(WarframeEventList.NotifyEvent.ID);
WarframeEventList.WarframeEvent[i].GUID:='';
WarframeEventList.WarframeEvent[i].ItemIndex:=-2;
WarframeEventList.WarframeEvent[i].EventType:=1;
WarframeEventList.WarframeEvent[i].Planet:=copy(S2, 1, pos(')', S2));
S2:=copy(S2, Length(WarframeEventList.WarframeEvent[i].Planet)+3, Length(S2)-Length(WarframeEventList.WarframeEvent[i].Planet));
WarframeEventList.WarframeEvent[i].Mission:=copy(S2, 1, pos(' -', S2)-1);
WarframeEventList.WarframeEvent[i].EventDate:=NewAlertDate;
S2:=copy(S2, Length(WarframeEventList.WarframeEvent[i].Mission)+4, Length(S2)-Length(WarframeEventList.WarframeEvent[i].Mission));
WarframeEventList.WarframeEvent[i].Time:=StrToInt(copy(S2, 1, pos('m', S2)-1))-1;
S2:=copy(S2, pos('-', S2)+2, Length(S2)-pos('-', S2));
WarframeEventList.WarframeEvent[i].RewardCredits:=StrToInt(copy(S2, 1, pos('cr', S2)-1));
WarframeEventList.WarframeEvent[i].RewardOther:=copy(S2, pos('cr', S2)+5, Length(S2)-pos('cr', S2));
WarframeEventList.WarframeEvent[i].RewardOtherAmount:=1;
WarframeEventList.WarframeEvent[i].Notified:=False;
WarframeEventList.WarframeEvent[i].Hidden:=False;
end;
self.Free;
end;
procedure TUpdateFromRSSThread.Execute;
var
HTTPClient: TIdHTTP;
IOHandler: TIdSSLIOHandlerSocketOpenSSL;
S, S2, S3: String;
i: Integer;
NewEventType: Byte;
ErrorLogFile: TextFile;
begin
HTTPClient:=TIdHTTP.Create(nil);
IOHandler:=TIdSSLIOHandlerSocketOpenSSL.Create(nil);
try
try
HTTPClient.IOHandler:=IOHandler;
HTTPClient.HandleRedirects:=True;
S:=HTTPClient.Get(self.RSS_URL);
except
Assign(ErrorLogFile, AppPath+'Error.log');
if not(FileExists(AppPath+'Error.log')) then
Rewrite(ErrorLogFile);
Append(ErrorLogFile);
Writeln(ErrorLogFile, DateTimeToStr(Now)+' '+LS_ErrorConnection+' '+self.RSS_URL+'; Error code: '+IntToStr(HTTPClient.ResponseCode));
Close(ErrorLogFile);
self.Terminate;
HTTPClient.Free;
IOHandler.Free;
end;
finally
HTTPClient.Free;
IOHandler.Free;
end;
if Application.Terminated or self.Terminated then exit;
for i:= 0 to WarframeEventList.WarframeEventCount-1 do
if (WarframeEventList.WarframeEvent[i].EventType=2) or (WarframeEventList.WarframeEvent[i].EventType=3) then
WarframeEventList.WarframeEvent[i].Time:=0;
while pos('<item>', S)>0 do begin
S:=copy(S, pos('<item>', S)+6, Length(S)-pos('<item>', S)-6);
S2:=LowerCase(copy(S, pos('<author>', S)+8, pos('</author>', S)-pos('<author>', S)-8));
NewEventType:=0;
if S2='alert' then
NewEventType:=1;
if S2='invasion' then
NewEventType:=2;
if S2='outbreak' then
NewEventType:=3;
if NewEventType=1 then
Continue;
S2:=LowerCase(copy(S, pos('<guid>', S)+6, pos('</guid>', S)-pos('<guid>', S)-6));
for i:= 0 to WarframeEventList.WarframeEventCount-1 do
if ((WarframeEventList.WarframeEvent[i].EventType=2) or (WarframeEventList.WarframeEvent[i].EventType=3)) and (WarframeEventList.WarframeEvent[i].GUID=S2) then begin
WarframeEventList.WarframeEvent[i].Time:=1;
NewEventType:=255;
end;
if NewEventType=255 then
Continue;
Inc(WarframeEventList.WarframeEventCount);
SetLength(WarframeEventList.WarframeEvent, WarframeEventList.WarframeEventCount);
WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].ItemIndex:=-2;
WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].GUID:=S2;
WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].EventType:=NewEventType;
WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].Time:=1;
WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].RewardOther:='';
WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].RewardOtherAmount:=0;
WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].RewardCredits:=0;
S2:=copy(S, pos('<title>', S)+7, pos('</title>', S)-pos('<title>', S)-7);
if NewEventType=2 then begin
S2:=Copy(S2, 1, pos(' - ', S2)-1);
WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].RewardOther:=S2;
S3:=Copy(S2, 1, pos('VS.', S2)-1);
S3:=Copy(S3, pos('(', S3), Length(S3)-pos('(', S3)+1);
if pos('x ', S3)>0 then
WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].RewardOtherAmount:=StrToInt(copy(S3, 2, pos('x ', S3)-2));
if pos('K)', S3)>0 then
WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].RewardCredits:=StrToInt(copy(S3, 2, pos('K)', S3)-2))*1000;
S3:=Copy(S2, pos('VS.', S2)+4, Length(S2)-pos('VS.', S2)-3);
S3:=Copy(S3, pos('(', S3), Length(S3)-pos('(', S3)+1);
if pos('x ', S3)>0 then
if WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].RewardOtherAmount<StrToInt(copy(S3, 2, pos('x ', S3)-2)) then
WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].RewardOtherAmount:=StrToInt(copy(S3, 2, pos('x ', S3)-2));
if pos('K)', S3)>0 then
if WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].RewardCredits<StrToInt(copy(S3, 2, pos('K)', S3)-2))*1000 then
WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].RewardCredits:=StrToInt(copy(S3, 2, pos('K)', S3)-2))*1000;
end;
if NewEventType=3 then begin
S2:=Copy(S2, 1, pos(' - ', S2)-1);
WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].RewardOther:='';
if pos('x ', S2)>0 then begin
WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].RewardOtherAmount:=StrToInt(copy(S2, 1, pos('x ', S2)-1));
WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].RewardOther:=S2;
end;
if copy(S2, Length(S2), 1)='K' then
WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].RewardCredits:=StrToInt(copy(S2, 1, Length(S2)-1))*1000;
S2:=copy(S2, 1, Length(S2)-1);
end;
S2:=copy(S, pos('<title>', S)+7, pos('</title>', S)-pos('<title>', S)-7);
WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].Planet:=Copy(S2, pos(' - ', S2)+3, Length(S2)-pos(' - ', S2));
WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].Mission:=copy(S, pos('<author>', S)+8, pos('</author>', S)-pos('<author>', S)-8);
WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].Notified:=False;
WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].Hidden:=False;
end;
self.Free;
end;
问题是:
1)有没有办法在不破坏/重新创建线程的情况下重新使用线程?毋庸置疑,这会导致内存碎片化,并且在一天的不间断工作后程序开始膨胀。 我不问如何制作循环线程,因为它们不适用于此处。
2)何时是免费线程的正确时间?我应该在他们完成工作之前还是在我在计时器处理程序(每分钟触发一次)之前创建它们之前释放它?或者在执行完成后可能会出现线程自杀?
3)我应该
HTTPClient: TIdHTTP;
IOHandler: TIdSSLIOHandlerSocketOpenSSL;
一个外部(全局)对象,以避免每分钟破坏/重新创建它们?
4)当我尝试关闭我的应用程序时,如果线程处于活动状态,它会死锁。有没有办法终止线程,即使他们没有完成他们的工作或可能分离它们?
答案 0 :(得分:5)
您在此处提出了很多问题,然后添加了大量代码。代码严重破坏。我可以看到:
Self.Free
内部对Execute
的错误调用。这完全是错的。如果你想要一个自我破坏的线程,你必须使用FreeOnTerminate
。HTTPClient
和IOHandler
。我确信您的代码中还有许多其他错误。
但我不想进入那个。而且我不想解决你直接提出的所有四个问题。我只限于一个人。我相信处理这个以及所有其他问题都会消失。
有没有办法在不破坏/重新创建它们的情况下重新使用线程?
在评论中你也说:
如果我在例外后没有将其释放并且每分钟拨打
Start
该怎么办?
在我看来,这表明你有根本的误解。线程的代码只不过是一个函数调用。在TThread
的情况下,该函数为Execute
。线程启动时,调用Execute
。当Execute
返回时,该线程的使用寿命结束了。 <{1}}返回后无法重新启动。
这意味着如果您希望单个线程执行单个任务的多次重复,那么您需要在Execute
方法中实现一个循环。
你还说:
我不问如何制作循环线程,因为它们不适用于此。
对不起,但那不对。使线程多次执行任务的方法是循环。
更一般地说,我觉得利用更高级别的并行库可以为您提供良好的服务。最好的是OTL。
如果你不想接受这个建议,那么我将如何构建你的程序:
Execute
方法中的标准while not Terminated
循环。Execute
,设置cancel事件,并在两个线程上调用Terminate
。然后应用程序可以安全终止。如果你像这样设计你的程序,所有其他问题就会消失。您只在程序的整个生命周期中创建两个线程。这涉及问题1,2和4.对于问题3,您将这些对象保留为局部变量。它们是线程的本地,应该是Free
下的本地人。或者更好的是,Execute
调用的辅助方法中的本地人。您的Execute
方法非常大。
与此分开,是列表中的数据竞争。我不能就如何解决这个问题给你详细的建议。显然需要一些序列化。
答案 1 :(得分:2)
Execute
内运行一个循环(而不是仅执行一次操作然后终止),并检查while not Terminated
或等待事件循环以允许干净关闭Terminate
或信号来表明他们应该结束他们的工作。如果FreeOnTerminate
为True,则主线程