[英]How do I abort/cancel TPL Tasks?
在一个线程中,我创建了一些System.Threading.Task
并启动了每个任务。
当我执行.Abort()
来终止线程时,任务不会中止。
如何将.Abort()
传输到我的任务?
你不能。 任务使用线程池中的后台线程。 也不推荐使用 Abort 方法取消线程。 您可以查看以下博客文章,其中解释了使用取消令牌取消任务的正确方法。 下面是一个例子:
class Program
{
static void Main()
{
var ts = new CancellationTokenSource();
CancellationToken ct = ts.Token;
Task.Factory.StartNew(() =>
{
while (true)
{
// do some heavy work here
Thread.Sleep(100);
if (ct.IsCancellationRequested)
{
// another thread decided to cancel
Console.WriteLine("task canceled");
break;
}
}
}, ct);
// Simulate waiting 3s for the task to complete
Thread.Sleep(3000);
// Can't wait anymore => cancel this task
ts.Cancel();
Console.ReadLine();
}
}
如果您捕获运行任务的线程,则很容易中止任务。 下面是一个示例代码来演示这一点:
void Main()
{
Thread thread = null;
Task t = Task.Run(() =>
{
//Capture the thread
thread = Thread.CurrentThread;
//Simulate work (usually from 3rd party code)
Thread.Sleep(1000);
//If you comment out thread.Abort(), then this will be displayed
Console.WriteLine("Task finished!");
});
//This is needed in the example to avoid thread being still NULL
Thread.Sleep(10);
//Cancel the task by aborting the thread
thread.Abort();
}
我使用 Task.Run() 来展示最常见的用例 - 使用带有旧单线程代码的 Tasks 的舒适性,它不使用 CancellationTokenSource 类来确定是否应该取消它。
就像这篇文章建议的那样,这可以通过以下方式完成:
int Foo(CancellationToken token)
{
Thread t = Thread.CurrentThread;
using (token.Register(t.Abort))
{
// compute-bound work here
}
}
虽然它有效,但不建议使用这种方法。 如果您可以控制在任务中执行的代码,则最好对取消进行适当的处理。
这种事情是Abort
被弃用的逻辑原因之一。 首先,如果可能,不要使用Thread.Abort()
取消或停止线程。 Abort()
应该只用于强行杀死一个没有响应更和平的请求的线程,以便及时停止。
话虽如此,您需要提供一个共享取消指示器,一个线程设置并等待,而另一个线程定期检查并正常退出。 .NET 4 包含一个专门为此目的设计的结构,即CancellationToken
。
要回答 Prerak K 关于在 Task.Factory.StartNew() 中不使用匿名方法时如何使用 CancellationTokens 的问题,您将 CancellationToken 作为参数传递到您使用 StartNew() 开始的方法中,如 MSDN 示例所示在这里。
例如
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
Task.Factory.StartNew( () => DoSomeWork(1, token), token);
static void DoSomeWork(int taskNum, CancellationToken ct)
{
// Do work here, checking and acting on ct.IsCancellationRequested where applicable,
}
您不应该尝试直接执行此操作。 设计您的任务以使用CancellationToken ,并以这种方式取消它们。
此外,我建议您也将主线程更改为通过 CancellationToken 运行。 调用Thread.Abort()
是一个坏主意 - 它会导致各种很难诊断的问题。 相反,该线程可以使用相同的取消,你的任务使用-和同CancellationTokenSource
可以用来触发所有任务的取消和你的主线程。
这将导致更简单、更安全的设计。
我使用混合方法取消任务。
查看下面的示例:
private CancellationTokenSource taskToken;
private AutoResetEvent awaitReplyOnRequestEvent = new AutoResetEvent(false);
void Main()
{
// Start a task which is doing nothing but sleeps 1s
LaunchTaskAsync();
Thread.Sleep(100);
// Stop the task
StopTask();
}
/// <summary>
/// Launch task in a new thread
/// </summary>
void LaunchTaskAsync()
{
taskToken = new CancellationTokenSource();
Task.Factory.StartNew(() =>
{
try
{ //Capture the thread
runningTaskThread = Thread.CurrentThread;
// Run the task
if (taskToken.IsCancellationRequested || !awaitReplyOnRequestEvent.WaitOne(10000))
return;
Console.WriteLine("Task finished!");
}
catch (Exception exc)
{
// Handle exception
}
}, taskToken.Token);
}
/// <summary>
/// Stop running task
/// </summary>
void StopTask()
{
// Attempt to cancel the task politely
if (taskToken != null)
{
if (taskToken.IsCancellationRequested)
return;
else
taskToken.Cancel();
}
// Notify a waiting thread that an event has occurred
if (awaitReplyOnRequestEvent != null)
awaitReplyOnRequestEvent.Set();
// If 1 sec later the task is still running, kill it cruelly
if (runningTaskThread != null)
{
try
{
runningTaskThread.Join(TimeSpan.FromSeconds(1));
}
catch (Exception ex)
{
runningTaskThread.Abort();
}
}
}
任务通过取消令牌具有一流的取消支持。 使用取消令牌创建您的任务,并通过这些明确取消任务。
您可以使用CancellationToken
来控制任务是否被取消。 您是在谈论在它开始之前中止它(“没关系,我已经这样做了”),还是实际上在中间中断了它? 如果是前者, CancellationToken
会有所帮助; 如果是后者,您可能需要实现自己的“救助”机制,并在任务执行的适当点检查是否应该快速失败(您仍然可以使用 CancellationToken 来帮助您,但需要更多手动操作)。
MSDN 有一篇关于取消任务的文章: http : //msdn.microsoft.com/en-us/library/dd997396.aspx
任务正在 ThreadPool 上执行(至少,如果您使用的是默认工厂),因此中止线程不会影响任务。 有关中止任务,请参阅 msdn 上的任务取消。
我试过CancellationTokenSource
但我不能这样做。 我确实用我自己的方式做到了这一点。 它有效。
namespace Blokick.Provider
{
public class SignalRConnectProvider
{
public SignalRConnectProvider()
{
}
public bool IsStopRequested { get; set; } = false; //1-)This is important and default `false`.
public async Task<string> ConnectTab()
{
string messageText = "";
for (int count = 1; count < 20; count++)
{
if (count == 1)
{
//Do stuff.
}
try
{
//Do stuff.
}
catch (Exception ex)
{
//Do stuff.
}
if (IsStopRequested) //3-)This is important. The control of the task stopping request. Must be true and in inside.
{
return messageText = "Task stopped."; //4-) And so return and exit the code and task.
}
if (Connected)
{
//Do stuff.
}
if (count == 19)
{
//Do stuff.
}
}
return messageText;
}
}
}
以及调用该方法的另一类:
namespace Blokick.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MessagePerson : ContentPage
{
SignalRConnectProvider signalR = new SignalRConnectProvider();
public MessagePerson()
{
InitializeComponent();
signalR.IsStopRequested = true; // 2-) And this. Make true if running the task and go inside if statement of the IsStopRequested property.
if (signalR.ChatHubProxy != null)
{
signalR.Disconnect();
}
LoadSignalRMessage();
}
}
}
如果可以使任务在其自己的线程上创建并在其Thread
对象上调用Abort
,则可以像线程一样中止任务。 默认情况下,任务在线程池线程或调用线程上运行 - 您通常不想中止这两个线程。
为确保任务获得自己的线程,请创建一个派生自TaskScheduler
的自定义调度程序。 在QueueTask
的实现中,创建一个新线程并使用它来执行任务。 稍后,您可以中止线程,这将导致任务在出现ThreadAbortException
的错误状态下完成。
使用此任务调度程序:
class SingleThreadTaskScheduler : TaskScheduler
{
public Thread TaskThread { get; private set; }
protected override void QueueTask(Task task)
{
TaskThread = new Thread(() => TryExecuteTask(task));
TaskThread.Start();
}
protected override IEnumerable<Task> GetScheduledTasks() => throw new NotSupportedException(); // Unused
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) => throw new NotSupportedException(); // Unused
}
像这样开始你的任务:
var scheduler = new SingleThreadTaskScheduler();
var task = Task.Factory.StartNew(action, cancellationToken, TaskCreationOptions.LongRunning, scheduler);
稍后,您可以中止:
scheduler.TaskThread.Abort();
应谨慎使用
Thread.Abort
方法。 特别是当你调用它来中止当前线程以外的线程时,你不知道抛出ThreadAbortException时哪些代码已经执行或执行失败,也无法确定应用程序的状态或任何应用程序和用户状态它负责保存。 例如,调用Thread.Abort
可能会阻止静态构造函数执行或阻止释放非托管资源。
您可以使用此 class..:它适用于所有类型的返回值..
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace CarNUChargeTester
{
public class TimeOutTaskRunner<T>
{
private Func<T> func;
private int sec;
private T result;
public TimeOutTaskRunner(Func<T> func, int sec)
{
this.func = func;
this.sec = sec;
}
public bool run()
{
var scheduler = new SingleThreadTaskScheduler();
Task<T> task = Task<T>.Factory.StartNew(func, (new CancellationTokenSource()).Token, TaskCreationOptions.LongRunning, scheduler);
if (!task.Wait(TimeSpan.FromSeconds(sec)))
{
scheduler.TaskThread.Abort();
return false;
}
result = task.Result;
return true;
}
public T getResult() { return result; }
}
class SingleThreadTaskScheduler : TaskScheduler
{
public Thread TaskThread { get; private set; }
protected override void QueueTask(Task task)
{
TaskThread = new Thread(() => TryExecuteTask(task));
TaskThread.Start();
}
protected override IEnumerable<Task> GetScheduledTasks() => throw new NotSupportedException();
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) => throw new NotSupportedException();
}
}
要使用它,你可以这样写:
TimeOutTaskRunner<string> tr = new TimeOutTaskRunner<string>(f, 10); // 10 sec to run f
if (!tr.run())
errorMsg("TimeOut"); !! My func
tr.getResult() // get the results if it done without timeout..
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.