delphi 协程 doroutine 取消协程、及取消回调
简介
协程 执行前、执行中、执行后 全部都可以被取消;
- 执行前自动取消;
- 执行中,是协程内核 优先尝试取消,并清空线程栈;若开发者内部是for循环大耗时协程,开发者自己也可以 通过 IsCancel 判断,来自己结束此协程;
- 执行后取消是指,可能此协程衍生出来了很多子协程,这些子协程又是有 前、中、后 3个状态;子协程会连带自动取消;
示例代码
unit main;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, Vcl.StdCtrls, doroutine, System.Generics.Collections;
type
TFormMain = class(TForm)
Button1: TButton;
Button3: TButton;
procedure Button1Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
FormMain: TFormMain;
n: Integer;
implementation
{$R *.dfm}
procedure doCanceled(go: TGo);
begin
if TThread.Current.ThreadID = MainThreadID then
begin
ShowMessage('主线程 执行了 取消回调');
end else begin
ShowMessage('bg线程 执行了 取消回调');
end;
end;
procedure bgSmall(go: TGo);
begin
//doSomeThing http 请求了,或其他 后台业务
Sleep(100); //模拟做事
//可看下日志
OutputDebugString(PChar(Format('干完了第: %d 个,后,由于被取消了,剩下的不干了!', [AtomicIncrement(n)])));
end;
procedure bgBig(go: TGo);
begin
//此大协程开启300个bg小协程做事
for var i := 1 to 300 do
begin
go.bg(bgSmall).start;
end;
end;
procedure TFormMain.Button1Click(Sender: TObject);
begin
//开启一个大协程做事,做的过程中,取消此大协程
var tid := go.bg(bgBig).onCancelUi(doCanceled).start;
Sleep(1000); //模拟让当前线程睡一会,让后台【线程们】执行一会,然后开始取消
gm.cancel(tid); //开始取消这个big协程,它衍生的300个小协程一并都会被取消;
end;
/// <summary>
/// 一个协程内部 又使用了 for 循环,且每次循环 都有耗时操作
/// </summary>
procedure forLoopBg(go: TGo);
begin
for var i := 1 to 100 do
begin
if go.isCancel then
begin
OutputDebugString(PChar(Format('循环到:%d后,我被取消了,跳出循环,退出此协程!', [i])));
Exit; //退出此协程
end;
Sleep(100); //模拟耗时做一些事,外层是一个 for 循环
end;
end;
procedure TFormMain.Button3Click(Sender: TObject);
begin
//启动一个协程内部有 for 循环 耗时的协程;
var tid := go.bg(forLoopBg).onCancelUi(doCanceled).start;
Sleep(1000); //模拟让当前线程睡一会,然后开始取消
gm.cancel(tid); //开始取消
end;
end.
效果图

留意
-
开发者应该尽量避免在一个协程内部,又写 【耗时】的 for/while/repeat 循环,不耗时的 for循环 你可以随便写;应该把这个耗时的循环协程,拆分成多个小协程;这个是 推荐的做法,因为这样运行效率是最高的,协程是并行的;
-
在耗时循环内部使用 isCancel 判断,若为 true 则退出循环,如上例里的:
/// <summary> /// 一个协程内部 又使用了 for 循环,且每次循环 都有耗时操作 /// </summary> procedure forLoopBg(go: TGo); begin for var i := 1 to 100 do begin if go.isCancel then begin OutputDebugString(PChar(Format('循环到:%d后,我被取消了,跳出循环,退出此协程!', [i]))); Exit; //退出此协程 end; Sleep(100); //模拟耗时做一些事,外层是一个 for 循环 end; end;

浙公网安备 33010602011771号