简介
协程 执行前、执行中、执行后 全部都可以被取消;
- 执行前取消;
- 执行中,是协程内核 优先尝试取消,并清空线程栈;若开发者内部是for循环大耗时协程,开发者自己也可以 通过 IsCancel 判断,来自己结束此协程;
- 执行后取消是指,可能此协程衍生出来了很多子协程,这些子协程又是有 前、中、后 3个状态;子协程会连带取消;
示例代码
unit main;interfaceusesWinapi.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;typeTForm3 = class(TForm)Button1: TButton;Button3: TButton;procedure Button1Click(Sender: TObject);procedure Button3Click(Sender: TObject);private{ Private declarations }public{ Public declarations }end;varForm3: TForm3;n: Integer;implementation{$R *.dfm}procedure doCanceled(task: TTask);
beginif TThread.Current.ThreadID = MainThreadID thenbeginShowMessage('主线程 执行了 取消回调');end else beginShowMessage('bg线程 执行了 取消回调');end;
end;procedure bgSmall(task: TTask);
begin//doSomeThing http 请求了,或其他 后台业务Sleep(100); //模拟做事//可看下日志OutputDebugString(PChar(Format('干完了第: %d 个,后,由于被取消了,剩下的不干了!', [AtomicIncrement(n)])));
end;procedure bgBig(task: TTask);
begin//此大协程开启300个bg小协程做事for var i := 1 to 300 dobegintask.bg(bgSmall).start;end;
end;procedure TForm3.Button1Click(Sender: TObject);
begin//开启一个大协程做事,做的过程中,取消此大协程var tid := task.bg(bgBig).onCancelUi(doCanceled).start;Sleep(1000); //模拟让当前线程睡一会,让后台【线程们】执行一会,然后开始取消tasks.cancel(tid); //开始取消这个big协程,它衍生的300个小协程一并都会被取消;
end;/// <summary>
/// 一个协程内部 又使用了 for 循环,且每次循环 都有耗时操作
/// </summary>
procedure forLoopBg(task: TTask);
beginfor var i := 1 to 100 dobeginif task.isCancel thenbeginOutputDebugString(PChar(Format('循环到:%d后,我被取消了,跳出循环,退出此协程!', [i])));Exit; //退出此协程end;Sleep(100); //模拟耗时做一些事,外层是一个 for 循环end;
end;procedure TForm3.Button3Click(Sender: TObject);
begin//启动一个协程内部有 for 循环 耗时的协程;var tid := task.bg(forLoopBg).onCancelUi(doCanceled).start;Sleep(1000); //模拟让当前线程睡一会,然后开始取消tasks.cancel(tid); //开始取消
end;end.
效果图
留意
-
开发者应该尽量避免在一个协程内部,又写 【耗时】的 for/while/repeat 循环,不耗时的 for循环 你可以随便写;应该把这个耗时的循环协程,拆分成多个小协程;这个是 推荐的做法,因为这样运行效率是最高的,协程是并行的;
-
在耗时循环内部使用 isCancel 判断,若为 true 则退出循环,如上例里的:
/// <summary> /// 一个协程内部 又使用了 for 循环,且每次循环 都有耗时操作 /// </summary> procedure forLoopBg(task: TTask); beginfor var i := 1 to 100 dobeginif task.isCancel thenbeginOutputDebugString(PChar(Format('循环到:%d后,我被取消了,跳出循环,退出此协程!', [i])));Exit; //退出此协程end;Sleep(100); //模拟耗时做一些事,外层是一个 for 循环end; end;