.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不具备被轮询的条件下,应该怎么样我才能够达到我预期的效果呢?
多谢指点。
...全文
2279 25 打赏 收藏 转发到动态 举报
AI 作业
写回复
用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)

111,097

社区成员

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

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

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