可以像中止一个Thread(Thread.Abort方法)一样中止一个Task吗?

时间:2010-12-05 16:49:03

标签: c# .net-4.0 parallel-processing

我们可以中止这样一个帖子:

Thread thread = new Thread(SomeMethod);
.
.
.
thread.Abort();

但是我可以以同样的方式中止任务(在.Net 4.0中)而不是取消机制。 我想立即杀死任务。

8 个答案:

答案 0 :(得分:39)

答案 1 :(得分:38)

  

但是我可以以同样的方式中止任务(在.Net 4.0中)而不是   取消机制。 我想立即杀死任务

其他回答者告诉过你不要这样做。但是,是的,你可以做到这一点。您可以提供Thread.Abort()作为由Task的取消机制调用的委托。以下是您可以配置的方法:

class HardAborter
{
  public bool WasAborted { get; private set; }
  private CancellationTokenSource Canceller { get; set; }
  private Task<object> Worker { get; set; }

  public void Start(Func<object> DoFunc)
  {
    WasAborted = false;

    // start a task with a means to do a hard abort (unsafe!)
    Canceller = new CancellationTokenSource();

    Worker = Task.Factory.StartNew(() => 
      {
        try
        {
          // specify this thread's Abort() as the cancel delegate
          using (Canceller.Token.Register(Thread.CurrentThread.Abort))
          {
            return DoFunc();
          }
        }
        catch (ThreadAbortException)
        {
          WasAborted = true;
          return false;
        }
      }, Canceller.Token);
  }

  public void Abort()
  {
    Canceller.Cancel();
  }

}

免责声明:不要这样做。

以下是不该做的示例:

 var doNotDoThis = new HardAborter();

 // start a thread writing to the console
 doNotDoThis.Start(() =>
    {
       while (true)
       {
          Thread.Sleep(100);
          Console.Write(".");
       }
       return null;
    });


 // wait a second to see some output and show the WasAborted value as false
 Thread.Sleep(1000);
 Console.WriteLine("WasAborted: " + doNotDoThis.WasAborted);

 // wait another second, abort, and print the time
 Thread.Sleep(1000);
 doNotDoThis.Abort();
 Console.WriteLine("Abort triggered at " + DateTime.Now);

 // wait until the abort finishes and print the time
 while (!doNotDoThis.WasAborted) { Thread.CurrentThread.Join(0); }
 Console.WriteLine("WasAborted: " + doNotDoThis.WasAborted + " at " + DateTime.Now);

 Console.ReadKey();

output from sample code

答案 2 :(得分:28)

  1. 您不应该使用Thread.Abort()
  2. 任务可以取消但不会中止。
  3. {严重]弃用Thread.Abort()方法。

    线程和任务在停止时应该合作,否则您将面临使系统处于不稳定/未定义状态的风险。

    如果您确实需要运行进程并从外部终止它,唯一安全的选择是在单独的AppDomain中运行它。

答案 3 :(得分:2)

虽然可以中止一个帖子,但实际上这样做几乎总是一个非常糟糕的主意。取消一个线程意味着该线程没有机会在其自身之后进行清理,使资源未被删除,以及处于未知状态的事物。

实际上,如果你中止一个线程,你应该只杀死一个进程。可悲的是,太多人认为ThreadAbort是一种可行的方法,可以阻止某些东西并继续下去,但事实并非如此。

由于Tasks以线程形式运行,你可以在它们上面调用ThreadAbort,但与通用线程一样,你几乎不想这样做,除非作为最后的手段。

答案 4 :(得分:0)

每个人都(希望)知道终止线程是不好的。问题是当您不拥有要调用的代码时。如果此代码在某些do / while无限循环中运行,其本身会调用某些本机函数,等等。当这种情况发生在您自己的代码终止,停止或Dispose调用中时,可以开始射击坏人(这样您就不会自己成为坏人)。

因此,就其价值而言,我已经编写了这两个使用各自的本机线程的阻塞函数,而不是池中的线程或CLR创建的某些线程。如果发生超时,它们将停止线程:

// returns true if the call went to completion successfully, false otherwise
public static bool RunWithAbort(this Action action, int milliseconds) => RunWithAbort(action, new TimeSpan(0, 0, 0, 0, milliseconds));
public static bool RunWithAbort(this Action action, TimeSpan delay)
{
    if (action == null)
        throw new ArgumentNullException(nameof(action));

    var source = new CancellationTokenSource(delay);
    var success = false;
    var handle = IntPtr.Zero;
    var fn = new Action(() =>
    {
        using (source.Token.Register(() => TerminateThread(handle, 0)))
        {
            action();
            success = true;
        }
    });

    handle = CreateThread(IntPtr.Zero, IntPtr.Zero, fn, IntPtr.Zero, 0, out var id);
    WaitForSingleObject(handle, 100 + (int)delay.TotalMilliseconds);
    CloseHandle(handle);
    return success;
}

// returns what's the function should return if the call went to completion successfully, default(T) otherwise
public static T RunWithAbort<T>(this Func<T> func, int milliseconds) => RunWithAbort(func, new TimeSpan(0, 0, 0, 0, milliseconds));
public static T RunWithAbort<T>(this Func<T> func, TimeSpan delay)
{
    if (func == null)
        throw new ArgumentNullException(nameof(func));

    var source = new CancellationTokenSource(delay);
    var item = default(T);
    var handle = IntPtr.Zero;
    var fn = new Action(() =>
    {
        using (source.Token.Register(() => TerminateThread(handle, 0)))
        {
            item = func();
        }
    });

    handle = CreateThread(IntPtr.Zero, IntPtr.Zero, fn, IntPtr.Zero, 0, out var id);
    WaitForSingleObject(handle, 100 + (int)delay.TotalMilliseconds);
    CloseHandle(handle);
    return item;
}

[DllImport("kernel32")]
private static extern bool TerminateThread(IntPtr hThread, int dwExitCode);

[DllImport("kernel32")]
private static extern IntPtr CreateThread(IntPtr lpThreadAttributes, IntPtr dwStackSize, Delegate lpStartAddress, IntPtr lpParameter, int dwCreationFlags, out int lpThreadId);

[DllImport("kernel32")]
private static extern bool CloseHandle(IntPtr hObject);

[DllImport("kernel32")]
private static extern int WaitForSingleObject(IntPtr hHandle, int dwMilliseconds);

答案 5 :(得分:-1)

您可以通过在您控制的线程上运行该任务并中止该线程来“中止”任务。这导致任务以错误状态ThreadAbortException完成。您可以使用自定义任务计划程序控制线程的创建,如this answer中所述。请注意,caveat about aborting a thread适用。

(如果不确保任务是在其自己的线程上创建的,则中止它会中止线程池线程或启动任务的线程,而这两个都不是您通常想要执行的操作。)

答案 6 :(得分:-2)

using System;
using System.Threading;
using System.Threading.Tasks;

...

var cts = new CancellationTokenSource();
var task = Task.Run(() => { while (true) { } });
Parallel.Invoke(() =>
{
    task.Wait(cts.Token);
}, () =>
{
    Thread.Sleep(1000);
    cts.Cancel();
});

这是一个使用CancellationTokenSource中止永无止境任务的简单代码段。

答案 7 :(得分:-3)

如果你有Task构造函数,那么我们可以从Task中提取Thread,并调用thread.abort。

Thread th = null;

Task.Factory.StartNew(() =>
{
    th = Thread.CurrentThread;

    while (true)
    {
        Console.WriteLine(DateTime.UtcNow);
    }
});

Thread.Sleep(2000);
th.Abort();
Console.ReadKey();