如何中止/取消TPL任务?

Pat*_*ier 144 .net c# multithreading task abort

在一个线程中,我创建一些System.Threading.Task并启动每个任务.

当我做一个.Abort()杀死线程时,任务不会中止.

我怎样才能传递.Abort()给我的任务?

Dar*_*rov 216

你不能.任务使用线程池中的后台线程.也不建议使用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();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 如果有一个阻塞调用在执行任务中没有返回怎么办? (57认同)
  • 很好的解释.我有一个问题,当我们在Task.Factory.StartNew中没有匿名方法时它是如何工作的?像Task.Factory.StartNew(()=> ProcessMyMethod(),cancellationToken) (5认同)
  • @ mehmet6parmak我认为你唯一可以做的就是使用`Task.Wait(TimeSpan/int)`从外面给它一个(基于时间的)截止日期. (2认同)
  • 如果我有我的自定义类来管理新“任务”中方法的执行怎么办?类似于:`public int StartNewTask(Action method)`。在 `StartNewTask` 方法中,我通过以下方式创建了一个新的 `Task`: `Task task = new Task(() => method()); task.Start();`。那么我如何管理`CancellationToken`?我还想知道是否像 `Thread` 我需要实现一个逻辑来检查是否有一些任务仍然挂起,因此在 `Form.Closing` 时将它们杀死。对于`Threads`,我使用`Thread.Abort()`。 (2认同)
  • 天啊,多么糟糕的例子啊!这是一个简单的布尔条件,当然这是第一个尝试的!但是,即我在一个单独的任务中有一个函数,这可能需要很长时间才能结束,理想情况下它不应该知道有关线程或其他什么的任何信息。那么根据您的建议我该如何取消该功能呢? (2认同)

Flo*_*ppl 30

如果捕获运行任务的线程,则可以轻松地中止任务.下面是一个示例代码来演示:

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();
}
Run Code Online (Sandbox Code Playgroud)

我使用Task.Run()来显示最常见的用例 - 使用旧的单线程代码使用Tasks,它不使用CancellationTokenSource类来确定是否应该取消它.

  • @Martin - 我已经在SO上研究了这个问题,我看到你已经投了几个使用Thread.Abort来杀死任务的答案,但你没有提供任何替代解决方案.**如何杀死不支持取消并在任务**中运行的第三方代码? (9认同)
  • AFAIK thread.abort会让你知道你的堆栈,它可能是无效的.我从来没有尝试过,但我想在单独的应用程序域中启动一个线程,thread.abort将保存!此外,在您的解决方案中浪费整个线程只是为了中止一个任务.您不必首先使用任务,而是使用线程.(downvote) (6认同)
  • 我认为这种方法可能会产生未知的后果,我不会在生产代码中推荐它.任务并不总是保证在不同的线程中执行,这意味着如果调度程序决定,它们可以在与创建它们的线程相同的线程中运行(这意味着主线程将被传递给`thread`局部变量).在您的代码中,您最终可能会中止主线程,这不是您真正想要的.如果你坚持中止,也许在中止之前检查线程是否相同是很好的想法 (6认同)
  • 谢谢你的想法.使用这种方法来实现某些外部代码的超时,它没有"CancellationToken"支持...... (2认同)

sta*_*ort 28

这篇文章建议的那样,这可以通过以下方式完成:

int Foo(CancellationToken token)
{
    Thread t = Thread.CurrentThread;
    using (token.Register(t.Abort))
    {
        // compute-bound work here
    }
}
Run Code Online (Sandbox Code Playgroud)

虽然它有效,但不建议使用这种方法.如果您可以控制在任务中执行的代码,则最好采用正确的取消处理方式.

  • +1表示其回退时提供不同的方法.我不知道这可以做:) (6认同)

Ada*_*son 17

这种事情是为什么Abort被弃用的后勤原因之一.首先,如果可能的话,不要使用Thread.Abort()取消或停止线程. Abort()应该只用于强制杀死一个没有及时响应更和平请求的线程.

话虽这么说,你需要提供一个共享取消指示器,一个线程设置并等待,而另一个线程定期检查并正常退出..NET 4包含专门为此目的而设计的结构CancellationToken.


Ree*_*sey 7

你不应该试着直接这样做.设计您的任务以使用CancellationToken,并以这种方式取消它们.

另外,我建议你也可以通过CancellationToken更改你的主线程.呼叫Thread.Abort()是一个坏主意 - 它可能导致各种难以诊断的问题.相反,该线程可以使用您的任务使用的相同取消 - 并且可以使用相同的取消CancellationTokenSource来触发所有任务和主线程的取消.

这将导致更简单,更安全的设计.


Joo*_*ols 7

要回答Prera​​k 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, 

}
Run Code Online (Sandbox Code Playgroud)


Ale*_*aus 6

我使用混合方法取消任务.

  • 首先,我想与使用礼貌地取消它取消.
  • 如果它仍然在运行(例如由于开发人员的错误),那么使用老式的Abort方法行为不端并将其杀死.

查看以下示例:

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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)