首页 新闻 会员 周边

c# 有什么办法立即停止一个TASK

0
悬赏园豆:10 [已解决问题] 解决于 2023-02-13 11:19

网上几百是如下模式来停止TASK:

Task Test(){
if (_cancelToken.IsCancellationRequested) {
return;
}

Sleep(1000)
.....
}
如果后面的逻辑需要执行10分钟猜完毕,岂不是要等待10分钟猜能正在取消?
这个cancelToken.IsCancellationRequested 不就相当于一个bool 标识符吗?不是鸡肋吗。。。。。。。

我的问题是有什么方法直接总结这个Test Task方法?

wgscd的主页 wgscd | 菜鸟二级 | 园豆:202
提问于:2022-12-18 22:18

cancelToken不是鸡肋,我经常用

会长 1年前

@会长: 能举个例子吗 谢谢!

wgscd 1年前
< >
分享
最佳答案
3

首先,从外部强制立即停止Task这种功能不存在,理由如下。
不要在Task中使用Thread.Sleep,用await Task.Delay代替。async Task会被编译器自动转换成状态机,代码是被切成一段一段的运行,每一段运行完成后会把下一段代码作为回调注册到任务调度器等待调度,调度器从线程池中选取空闲线程执行就绪的任务。因此异步方法的每一段都可能在不同的线程中运行,强制线程睡眠时是不知道哪个线程被睡眠的,会拉高死锁风险。使用Task后哪个线程何时被调度去执行哪个任务是不确定的,强制终止线程时根本不知道什么东西会被终止,因此Task根本不允许开发者访问其内部管理的线程池,就是怕出问题。
Task是协程的一种实现,协程的协指协作,因此任务的取消也应该是协作式取消,即外部可以请求取消任务,但不能强制终止任务。任务应根据取消令牌的状态在合适的时机完成善后再自行结束。这也避免强制结束导致内存数据处于错误状态或资源得不到正确清理影响程序正常工作。
用Task开发就应该把整个思路转过来,不要想怎么从外部掐断代码运行,应该反过来想内部可以在什么地方取消,应该怎么善后和自行结束。如果一个10分钟才能完成的工作,那应该考虑如何在收到取消请求后清理资源,在什么时候可以取消任务。如果是循环代码,可以考虑在循环开始或结尾检查令牌,对于顺序代码应该在合适地方添加检查代码和相应的善后代码。

收获园豆:10
coredx | 小虾三级 |园豆:678 | 2023-01-17 20:17

你这个回答我很满意 ,感谢!!!!

wgscd | 园豆:202 (菜鸟二级) | 2023-02-13 11:18

Sleep(1000)
.....
这个sleep困扰了好久,这个答案很好,尤其后面对Task的开发思路介绍

望涯断路 | 园豆:200 (初学一级) | 2023-10-08 06:44
其他回答(6)
0
中华鲟3670 | 园豆:847 (小虾三级) | 2022-12-19 09:46

感谢回复!只是你的例子不能立即取消!
Thread.Sleep(1000); 如果业务逻辑是需要 30分钟呢! Thread.Sleep(10003060); 岂不是等待30分钟才。。。。。!!!

支持(0) 反对(0) wgscd | 园豆:202 (菜鸟二级) | 2022-12-22 16:08
0

下面的试验代码可以成功取消 Task

var cts = new CancellationTokenSource();
cts.CancelAfter(10);
await Test(cts.Token);

async Task Test(CancellationToken cancelToken)
{
    Console.WriteLine("Sleeping...");
    await Task.Delay(5000, cancelToken);
    Console.WriteLine("Waking up...");
}
dudu | 园豆:31003 (高人七级) | 2022-12-19 11:04

感谢回复!只是你的例子和我说的不是一个意思!
比如加个 thread. sleep(5000*5000)就是我说的情况了:

Console.WriteLine("Sleeping...");
thread. sleep(5000*5000);
await Task.Delay(5000, cancelToken);
Console.WriteLine("Waking up...");

支持(0) 反对(0) wgscd | 园豆:202 (菜鸟二级) | 2022-12-22 16:05

@wgscd: Thread.Sleep 会阻塞当前线程

支持(0) 反对(0) dudu | 园豆:31003 (高人七级) | 2022-12-22 17:30
0

愚见勿喷:调用cmd,使用taskkill命令?

yuitoTDF | 园豆:216 (菜鸟二级) | 2022-12-29 15:29
0

原来的方式:
1、thread.Abort();thread.join();
2、taskcancel.cancel();
3、自己用循环监听变量的方式。

第1种是你要的方式,.net 4.8里面你可以试一下,主线程执行thread.abort的时候,子线子当前执行位置会抛出abort异常,从而终止线程,并且在Catch里面给子线程处理状态的机会。但是.net core里面不允许 使用了。
第2种是.net core推荐的方式(第3种是第2种的自己实现方式)

为什么不支持第1种了呢,因为微软说了,你不应该去终止一个子线程,因为你不知道子线程当时在干啥,也不知道它有多少资源,在操作系统层面无法干净的释放它。微软的建议是,如果你的功能性函数都是异步 的话,建议你用异步。但是,明显,你功能性的函数可能不是异步的,也就是你必须要Thread.Sleep(5000),而不是Task.Delay(5000),所以,你这种情况,微软建议你用多进程,而非多线程,因为进程被终止时,操作系统可以通过cbd释放它相关的所有资源。

ensleep | 园豆:1682 (小虾三级) | 2023-01-02 20:13
0

其实接触过下位机的人可以清楚的了解这是个扫描时序的问题
简单粗暴的还是做好每一段代码的管控,哪段可能return掉就加那段。
不然的话可能会容易出问题
但是也可能存在更好的办法我不知道。

_LI | 园豆:20 (初学一级) | 2023-01-29 16:57
0

如果是执行时间很长的任务可以在代码中插入一些检测结束标志的代码,如果只是sleep很长时间,for(1分钟*n){检测取消}

_尽余欢 | 园豆:202 (菜鸟二级) | 2023-03-05 16:24
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册