.net4.5基于task能否取消长时间运行(内部没有循环)的任务?

黎大 2018-02-05 10:57:11
如题所示,请各位高手耐心听我诉说一下我遇到的问题。

在我的主程序中,需要处理一个不确定费时多久的工作(有可能1ms完成,也有可能50s,但是是一段顺序执行的代码,只跑一轮,没有循环),我希望当这个工作执行超过一定时间(例如3s)之后,就被停止。
我把这个工作放到business类的Hardwork()方法中去做,并被主程序调用。

static void Main(string[] args)
{
business.Hardwork();
// 预期效果是,Hardwork执行完毕后,如果在3s内完成,退回主程序,如果3s没有完成,在3s时强制退出
Console.WriteLine("\nBACK TO THE MAIN PROCESS!");
Console.ReadLine();
}


Hardwork的实现如下

class business {
static bool bok = false; // 标识 复杂任务是否已经执行完成
static bool br = false; // 标识 复杂任务是否执行中
public static bool Hardwork()
{
var cts = new CancellationTokenSource(); //增加取消消息源
Task hardtask = Task.Run(() => { //启动这个hardtask,并使用 cts.token来接受取消的消息
int i=0;
while(!bok) { //
if (cts.Token.IsCancellationRequested)
cts.Token.ThrowIfCancellationRequested();
if (!br) // 如果费时的工作还没有运行,则执行如下,执行完毕后会更改bok的状态。
{
// 下面这个是费时工作,业务中是顺序执行,没办法轮询
Task.Factory.StartNew(() => { //这个就是费时的工作,结束后其会把bok设置为true,由于是顺序执行没办法进行状态的轮询啊?
br = true;
Console.WriteLine("hard working");
Thread.Sleep(5000);
Console.WriteLine("hard work finished");
bok = true;
Console.WriteLine("hw bok:" + bok);
});
}

Thread.Sleep(100); //休息100ms,打印一下bok是否完成状态,并去检查一下cts.Token.IsCancellationRequested
Console.WriteLine(i++ + "bok: " + bok);
}
Console.WriteLine("fffinish");

}, cts.Token);

cts.CancelAfter(3000); // 3000ms以后,发出取消任务
try
{
hardtask.Wait(); //等待hardtask完成,约3000ms后收到完成信号,退出。
}
catch (AggregateException ae)
{
foreach (Exception e in ae.InnerExceptions)
{
if (e is TaskCanceledException)
Console.WriteLine(e.GetType() +": {0}",
((TaskCanceledException)e).Message);
else
Console.WriteLine("Exception: " + e.GetType().Name);
}
//Console.WriteLine(ae.GetType() + ae.Message);
//Console.WriteLine(task.Status);
}
Console.WriteLine("hardtask:" + t2.Status);
Console.WriteLine("bokk:" + bok);
return bok;
}
}

上面代码执行后,结果如下,绿色注释部分是我的备注,也是困惑的地方:
[code=c#]
hard working
0bok: False
1bok: False
... ...
... ...
28bok: False
29bok: False
30bok: False
System.Threading.Tasks.TaskCanceledException: 已取消一个任务。//3ms的时候收到取消的信号,并抛出了异常
hardtask:Canceled //这里也说明hardtask任务已经取消了
bokk:False

BACK TO THE MAIN PROCESS! //这里说明已经返回了主程序,似乎一切挺好的
//然而又过了几秒,突然一切都不好了,business.Hardwork里面的那个费时的task在Hardwork方法已经结束的情况下,竟然没有杀死,此时又返回值了,而我是想让他死的啊。。。。
hard work finished
hw bok:True
[/code]

按照上面的结果看,方法即使退出了,里面的未完成task并没有被立即杀死啊。而在这个费时task不具备被轮询的条件下,应该怎么样我才能够达到我预期的效果呢?
多谢指点。
...全文
2207 25 打赏 收藏 转发到动态 举报
写回复
用AI写文章
25 条回复
切换为时间正序
请发表友善的回复…
发表回复
StephenFaust 2020-03-06
  • 打赏
  • 举报
回复
你可以把花时间的业务写成一个方法,然后用异步委托回调,很好用的
黎大 2018-02-14
  • 打赏
  • 举报
回复
backgroundworker有没有熟的? 这个是不是不应该被频繁的创建呢?
秋的红果实 2018-02-06
  • 打赏
  • 举报
回复
timer是专门解决你的问题的,给你样例

private System.Threading.Timer t;

private void cancelJob(object obj)
{
    Thread tempT = (Thread)obj;
    if(tempT.IsAlive)
    {
        if (listBox1.InvokeRequired)
        {
            listBox1.Invoke(new Action(() => listBox1.Items.Add("作业将被取消...")));
        }
        t.Dispose();

        //做好强行中断线程前的准备工作,如回滚业务逻辑等
        tempT.Abort();

    }
}

private void bigJob()
{
    if(listBox1.InvokeRequired)
    {
        listBox1.Invoke(new Action(() => listBox1.Items.Add("开始处理大型作业...")));
    }
    
    Thread.Sleep(5000); //模拟耗时操作
}

private void button1_Click(object sender, EventArgs e)
{
    Thread asyncJob = new Thread(bigJob);
    asyncJob.Start();

    TimerCallback tCallBack = new TimerCallback(cancelJob);
    t = new System.Threading.Timer(tCallBack, asyncJob, 3000, 0); //3秒后启动回调函数tCallBack

    listBox1.Items.Add("主线程处理其他业务中...");

}

效果
xuzuning 2018-02-06
  • 打赏
  • 举报
回复
两者的区别在于 https://blogs.msdn.microsoft.com/pfxteam/2011/10/24/task-run-vs-task-factory-startnew/ 英文的慢慢消化吧 我的理解就是:task.run 中默认不能再启动一个 task 任务,而 task.factory.startnew 可以 至于换了 CancelAfter,是因为4.0没有这个方法。不是问题的原因(我只有4.0环境) 由于你违规在前,所以出现不可预知的问题是在所必然的。
引用 20 楼 peterlee1983 的回复:
[quote=引用 15 楼 xuzuning 的回复:] 稍加调整,在 C#4.0 中没有任何问题 Task.Factory.StartNew( 换成 Task.Factory.StartNew( cts.CancelAfter(3000); 换成 Task.Factory.StartNew(() => { Thread.Sleep(3000); cts.Cancel(); });
版主威武,请问这个是什么原理,出处在哪里呢? 确实挺好用的! [/quote]
sexfio 2018-02-05
  • 打赏
  • 举报
回复
Task.Run在4.0里没有吧?
  • 打赏
  • 举报
回复
你是Task里面再执行Task,外部的Task被关掉了,里面的Task又没CancellationToken,你为啥里面还要用Task呢?
SoulRed 2018-02-05
  • 打赏
  • 举报
回复
EndInvoke(BeginInvoke(Func<> xxx)) 或者用coroutine 协程来优化你的程序。很好用
  • 打赏
  • 举报
回复
Task.WaitAny(task,Task.Run(()=>{Thread.Sleep(500000);}))
根据返回的index确认是哪个task执行完了,如果不是你的task,则设置CancellationToken
黎大 2018-02-05
  • 打赏
  • 举报
回复
引用 15 楼 xuzuning 的回复:
稍加调整,在 C#4.0 中没有任何问题 Task.Factory.StartNew( 换成 Task.Factory.StartNew( cts.CancelAfter(3000); 换成 Task.Factory.StartNew(() => { Thread.Sleep(3000); cts.Cancel(); });
版主威武,请问这个是什么原理,出处在哪里呢? 确实挺好用的!
泡泡龙 2018-02-05
  • 打赏
  • 举报
回复
CancellationToken到时间会抛出一个错误(需要加一句代码,自己百度),在你的task里面catch这个错误就知道到时了
黎大 2018-02-05
  • 打赏
  • 举报
回复
引用 15 楼 xuzuning 的回复:
稍加调整,在 C#4.0 中没有任何问题 Task.Factory.StartNew( 换成 Task.Factory.StartNew( cts.CancelAfter(3000); 换成 Task.Factory.StartNew(() => { Thread.Sleep(3000); cts.Cancel(); });
问题暂时看解决了,多谢老兄。
黎大 2018-02-05
  • 打赏
  • 举报
回复
引用 15 楼 xuzuning 的回复:
稍加调整,在 C#4.0 中没有任何问题 Task.Factory.StartNew( 换成 Task.Factory.StartNew( cts.CancelAfter(3000); 换成 Task.Factory.StartNew(() => { Thread.Sleep(3000); cts.Cancel(); });
哇塞,老哥,您这个方法好像管用啦
黎大 2018-02-05
  • 打赏
  • 举报
回复
引用 13 楼 xinbada1985 的回复:
[quote=引用 12 楼 peterlee1983 的回复:] [quote=引用 11 楼 xinbada1985 的回复:] 因为你虽然提出线程停止,但是因为线程还在执行过程中,所以只能是等线程执行完成后,才会停止,这个地方最好的办法是判断一下线程是否结束,没有结束等待一下,等待结束后在往下进行,线程最好不要强制终止,让他自然完成操作,不然会引起其他的一系列问题。
这一系列问题包括啥呢? task应该是属于clr的吧?这样的话垃圾收集什么的会不会好一些?[/quote] 一.资源释放问题,有资源占用空间,用垃圾收集是释放不了的 二.线程相关周边的操作的完整性,因为你的线程是非正常关闭,那么和这个线程相关了的操作是否受到影响。 三.线程动作完整性,某些操作做了一半没有完成就终止了,反映出来的一些相关信息,正确性没法保证。[/quote] 谢谢耐心解答,可是我现在的问题在于,这个费时的任务 可能会执行很久,我不想等了,想直接干掉他。
xuzuning 2018-02-05
  • 打赏
  • 举报
回复
稍加调整,在 C#4.0 中没有任何问题 Task.Factory.StartNew( 换成 Task.Factory.StartNew( cts.CancelAfter(3000); 换成 Task.Factory.StartNew(() => { Thread.Sleep(3000); cts.Cancel(); });
Jiang_Mr 2018-02-05
  • 打赏
  • 举报
回复
用 task 执行耗时的操作,是需要使用task 的返回值嘛?如果不用,换成Thread 呢?
xinbada1985 2018-02-05
  • 打赏
  • 举报
回复
引用 12 楼 peterlee1983 的回复:
[quote=引用 11 楼 xinbada1985 的回复:] 因为你虽然提出线程停止,但是因为线程还在执行过程中,所以只能是等线程执行完成后,才会停止,这个地方最好的办法是判断一下线程是否结束,没有结束等待一下,等待结束后在往下进行,线程最好不要强制终止,让他自然完成操作,不然会引起其他的一系列问题。
这一系列问题包括啥呢? task应该是属于clr的吧?这样的话垃圾收集什么的会不会好一些?[/quote] 一.资源释放问题,有资源占用空间,用垃圾收集是释放不了的 二.线程相关周边的操作的完整性,因为你的线程是非正常关闭,那么和这个线程相关了的操作是否受到影响。 三.线程动作完整性,某些操作做了一半没有完成就终止了,反映出来的一些相关信息,正确性没法保证。
黎大 2018-02-05
  • 打赏
  • 举报
回复
引用 11 楼 xinbada1985 的回复:
因为你虽然提出线程停止,但是因为线程还在执行过程中,所以只能是等线程执行完成后,才会停止,这个地方最好的办法是判断一下线程是否结束,没有结束等待一下,等待结束后在往下进行,线程最好不要强制终止,让他自然完成操作,不然会引起其他的一系列问题。
这一系列问题包括啥呢? task应该是属于clr的吧?这样的话垃圾收集什么的会不会好一些?
xinbada1985 2018-02-05
  • 打赏
  • 举报
回复
因为你虽然提出线程停止,但是因为线程还在执行过程中,所以只能是等线程执行完成后,才会停止,这个地方最好的办法是判断一下线程是否结束,没有结束等待一下,等待结束后在往下进行,线程最好不要强制终止,让他自然完成操作,不然会引起其他的一系列问题。
大鱼> 2018-02-05
  • 打赏
  • 举报
回复
引用 9 楼 peterlee1983 的回复:
[quote=引用 2 楼 DOwnstairs 的回复:] EndInvoke(BeginInvoke(Func<> xxx)) 或者用coroutine 协程来优化你的程序。很好用
谢谢您的线索,请问有没有例子。多谢了。
引用 8 楼 qq_17486399 的回复:
threadpool呢? 有没有此类功能。。。 这么看来这个task对我来说还鸡肋了。。。 Task本身是不支持取消的,如果取消会造成内存溢出等意外情况发生,所以不建议取消的,如果你确实业务必须要在执行过程中取消任务的话可以尝试用Thread[/quote] 可以用一个集合:比如List<Thread>
黎大 2018-02-05
  • 打赏
  • 举报
回复
引用 2 楼 DOwnstairs 的回复:
EndInvoke(BeginInvoke(Func<> xxx)) 或者用coroutine 协程来优化你的程序。很好用
谢谢您的线索,请问有没有例子。多谢了。
引用 8 楼 qq_17486399 的回复:
threadpool呢? 有没有此类功能。。。 这么看来这个task对我来说还鸡肋了。。。 Task本身是不支持取消的,如果取消会造成内存溢出等意外情况发生,所以不建议取消的,如果你确实业务必须要在执行过程中取消任务的话可以尝试用Thread
加载更多回复(4)
C#5.0本质论第四版,高清扫描的,对C#5.0技术讲的比较详细,第1章 C#概述 1 1.1 Hello,World 1 1.2 C#语法基础 3 1.2.1 C#关键字 3 1.2.2 标识符 4 1.2.3 类型定义 5 1.2.4 Main 6 1.2.5 语句和语句分隔符 7 1.2.6 空白 8 1.2.7 使用变量 8 1.2.8 数据类型 9 1.2.9 变量的声明 9 1.2.10 变量的赋值 10 1.2.11 变量的使用 11 1.3 控制台输入和输出 11 1.3.1 从控制台获取输入 11 1.3.2 将输出写入控制台 12 1.3.3 注释 14 1.3.4 托管执行和公共语言基础结构 16 1.3.5 C#和.NET版本 17 1.3.6 CIL和ILDASM 18 1.4 小结 20 第2章 数据类型 21 2.1 基本数值类型 21 2.1.1 整数类型 22 2.1.2 浮点类型 23 2.1.3 decimal类型 23 2.1.4 字面值 24 2.2 更多基本类型 27 2.2.1 布尔类型 27 2.2.2 字符类型 27 2.2.3 字符串 29 2.3 null和void 34 2.3.1 null 34 2.3.2 void 35 2.4 类型的分类 37 2.4.1 值类型 37 2.4.2 引用类型 37 2.5 可空修饰符 38 2.6 数据类型之间的转换 39 2.6.1 显式转型 39 2.6.2 隐式转型 41 2.6.3 不使用转型操作符的类型转换 42 2.7 数组 43 2.7.1 数组的声明 44 2.7.2 数组的实例化和赋值 45 2.7.3 数组的使用 48 2.7.4 字符串作为数组使用 52 2.7.5 常见数组错误 53 2.8 小结 55 第3章 操作符和控制流 57 3.1 操作符 57 3.1.1 一元操作符正和负 58 3.1.2 二元算术操作符 58 3.1.3 复合赋值操作符 64 3.1.4 递增和递减操作符 65 3.1.5 常量表达式和常量符号 68 3.2 控制流程概述 69 3.2.1 if语句 70 3.2.2 嵌套if 71 3.3 代码块 73 3.4 代码块、作用域和声明空间 74 3.5 布尔表达式 76 3.5.1 关系操作符和相等性操作符 77 3.5.2 逻辑布尔操作符 77 3.5.3 逻辑求反操作符 78 3.5.4 条件操作符 79 3.5.5 空接合操作符 80 3.6 按位操作符 80 3.6.1 移位操作符 81 3.6.2 按位操作符 82 3.6.3 按位赋值操作符 83 3.6.4 按位取反操作符 84 3.7 控制流语句(续) 84 3.7.1 while和do while循环 84 3.7.2 for循环 86 3.7.3 foreach循环 88 3.7.4 switch语句 90 3.8 跳转语句 92 3.8.1 break语句 92 3.8.2 continue语句 94 3.8.3 goto语句 95 3.9 C#预处理指令 97 3.9.1 排除和包含代码 98 3.9.2 定义预处理符号 98 3.9.3 生成错误和警告 99 3.9.4 关闭警告消息 99 3.9.5 nowarn:选项 99 3.9.6 指定行号 100 3.9.7 可视编辑器提示 100 3.10 小结 101 第4章 方法和参数 103 4.1 方法的调用 104 4.1.1 命名空间 105 4.1.2 类型名称 106 4.1.3 作用域 107 4.1.4 方法名称 107 4.1.5 形参和实参 107 4.1.6 方法返回值 107 4.1.7 语句与方法调用的比较 108 4.2 方法的声明 108 4.2.1 形式参数声明 109 4.2.2 方法返回类型声明 110 4.3 using指令 111 4.4 Main()的返回值和参数 114 4.5 方法的参数 116 4.5.1 值参数 116 4.5.2 引用参数(ref) 117 4.5.3 输出参数(out) 118 4.5.4 参数数组 120 4.6 递归 122 4.7 方法重载 124 4.8 可选参数 126 4.9 用异常实现基本错误处理 129 4.9.1 捕捉错误 130 4.9.2 使用throw语句报告错误 136 4.10 小结 138 第5章 类 139 5.1 类的定义和实例化 141 5.2 实例字段 144 5.2.1 实例字段的声明 144 5.2.2 实例字段的访问 144 5.3 实例方法 145 5.4 使用this关键字 146 5.5 访问修饰符 151 5.6 属性 153

110,502

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧