如何像中止线程一样中止任务(Thread.Abort 方法)?

发布于 2024-10-05 18:49:47 字数 218 浏览 1 评论 0原文

我们可以像这样中止一个Thread

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

但是我可以以同样的方式而不是通过取消机制来中止一个Task(在.Net 4.0中)吗? 我想立即终止任务。

We could abort a Thread like this:

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

But can I abort a Task (in .Net 4.0) in the same way not by cancellation mechanism. I want to kill the Task immediately.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

凉薄对峙 2024-10-12 18:49:47

关于不使用线程中止的指导是有争议的。我认为它仍然有一席之地,但在特殊情况下。但是,您应该始终尝试围绕它进行设计,并将其视为最后的手段。

例子;

您有一个简单的 Windows 窗体应用程序,它连接到阻塞同步 Web 服务。它在并行循环中执行 Web 服务上的函数。

CancellationTokenSource cts = new CancellationTokenSource();
ParallelOptions po = new ParallelOptions();
po.CancellationToken = cts.Token;
po.MaxDegreeOfParallelism = System.Environment.ProcessorCount;

Parallel.ForEach(iListOfItems, po, (item, loopState) =>
{

    Thread.Sleep(120000); // pretend web service call

});

在此示例中,阻塞调用需要 2 分钟才能完成。现在我将 MaxDegreeOfParallelism 设置为 ProcessorCount。 iListOfItems 有 1000 个项目需要处理。

用户单击进程按钮,循环开始,我们最多有 20 个线程针对 iListOfItems 集合中的 1000 个项目执行。每次迭代都在其自己的线程上执行。每个线程在由 Parallel.ForEach 创建时都将利用一个前台线程。这意味着无论主应用程序关闭如何,应用程序域都将保持活动状态,直到所有线程完成。

但是,用户由于某种原因需要关闭应用程序,例如关闭表单。
这 20 个线程将继续执行,直到处理完所有 1000 个项目。在这种情况下,这并不理想,因为应用程序不会按用户期望的那样退出,而是会继续在幕后运行,通过查看任务管理器可以看出。

假设用户尝试再次重建应用程序(VS 2010),它报告 exe 被锁定,然后他们必须进入任务管理器来终止它,或者只是等待所有 1000 个项目都被处理。

我不会责怪你这么说,但当然!我应该使用 CancellationTokenSource 对象并调用 Cancel ...但是从 .net 4.0 开始,这存在一些问题。首先,这仍然永远不会导致线程中止,这会在线程终止后提供中止异常,因此应用程序域将需要等待线程正常完成,这意味着等待最后一个阻塞调用,这将是最终调用 po.CancellationToken.ThrowIfCancellationRequested 的最后一个正在运行的迭代(线程)。
在示例中,这意味着应用程序域仍然可以保持活动状态长达 2 分钟,即使表单已关闭并取消调用。

请注意,在 CancellationTokenSource 上调用 Cancel 不会在处理线程上引发异常,这实际上会中断阻塞调用,类似于线程中止并停止执行。异常会被缓存,以便当所有其他线程(并发迭代)最终完成并返回时,该异常将在启动线程(声明循环的位置)中抛出。

我选择使用

相反,实现一个简单的线程安全属性(即 Bool stopExecuting)可以说“更好”。然后在循环中检查stopExecuting的值,如果该值因外部影响而设置为true,我们可以采取替代路径优雅地关闭。由于我们不应该调用取消,这会阻止检查 CancellationTokenSource.IsCancellationRequested 这将是另一种选择。

类似下面的 if 条件在循环中是合适的;

if (loopState.ShouldExitCurrentIteration || LoopState.IsExceptional || stopExecuting) {loopState.Stop();返回;}

,迭代将以“受控”方式退出,并终止进一步的迭代,但正如我所说,这对于我们必须等待长时间运行和阻塞调用的问题没有什么帮助。每次迭代(并行循环线程),因为这些必须在每个线程可以选择检查是否应该停止之前完成。

总之,当用户关闭表单时,20 个线程将通过 stopExecuting 收到停止信号,但它们只有在执行完长时间运行的函数调用后才会停止。

对于应用程序域将始终保持活动状态并且仅在所有前台线程完成后才被释放的事实,我们无能为力。这意味着等待循环内进行的任何阻塞调用完成都会产生延迟。

只有真正的线程中止才能中断阻塞调用,并且您必须在中止线程的异常处理程序中尽最大努力减轻系统处于不稳定/未定义状态的情况,这是毫无疑问的。这是否合适是程序员需要根据他们选择维护的资源句柄以及在线程的finally块中关闭它们的容易程度来决定的。您可以注册一个令牌以在取消时终止,作为半解决方法,

CancellationTokenSource cts = new CancellationTokenSource();
ParallelOptions po = new ParallelOptions();
po.CancellationToken = cts.Token;
po.MaxDegreeOfParallelism = System.Environment.ProcessorCount;

Parallel.ForEach(iListOfItems, po, (item, loopState) =>
{

    using (cts.Token.Register(Thread.CurrentThread.Abort))
    {
        Try
        {
           Thread.Sleep(120000); // pretend web service call          
        }
        Catch(ThreadAbortException ex)
        {
           // log etc.
        }
        Finally
        {
          // clean up here
        }
    }

});

但这仍然会导致声明线程中出现异常。

考虑到所有因素,使用parallel.loop 构造的中断阻塞调用可能是选项上的一种方法,避免使用库中更模糊的部分。但为什么在声明方法中没有取消和避免抛出异常的选项,我觉得这可能是一个疏忽。

The guidance on not using a thread abort is controversial. I think there is still a place for it but in exceptional circumstance. However you should always attempt to design around it and see it as a last resort.

Example;

You have a simple windows form application that connects to a blocking synchronous web service. Within which it executes a function on the web service within a Parallel loop.

CancellationTokenSource cts = new CancellationTokenSource();
ParallelOptions po = new ParallelOptions();
po.CancellationToken = cts.Token;
po.MaxDegreeOfParallelism = System.Environment.ProcessorCount;

Parallel.ForEach(iListOfItems, po, (item, loopState) =>
{

    Thread.Sleep(120000); // pretend web service call

});

Say in this example, the blocking call takes 2 mins to complete. Now I set my MaxDegreeOfParallelism to say ProcessorCount. iListOfItems has 1000 items within it to process.

The user clicks the process button and the loop commences, we have 'up-to' 20 threads executing against 1000 items in the iListOfItems collection. Each iteration executes on its own thread. Each thread will utilise a foreground thread when created by Parallel.ForEach. This means regardless of the main application shutdown, the app domain will be kept alive until all threads have finished.

However the user needs to close the application for some reason, say they close the form.
These 20 threads will continue to execute until all 1000 items are processed. This is not ideal in this scenario, as the application will not exit as the user expects and will continue to run behind the scenes, as can be seen by taking a look in task manger.

Say the user tries to rebuild the app again (VS 2010), it reports the exe is locked, then they would have to go into task manager to kill it or just wait until all 1000 items are processed.

I would not blame you for saying, but of course! I should be cancelling these threads using the CancellationTokenSource object and calling Cancel ... but there are some problems with this as of .net 4.0. Firstly this is still never going to result in a thread abort which would offer up an abort exception followed by thread termination, so the app domain will instead need to wait for the threads to finish normally, and this means waiting for the last blocking call, which would be the very last running iteration (thread) that ultimately gets to call po.CancellationToken.ThrowIfCancellationRequested.
In the example this would mean the app domain could still stay alive for up to 2 mins, even though the form has been closed and cancel called.

Note that Calling Cancel on CancellationTokenSource does not throw an exception on the processing thread(s), which would indeed act to interrupt the blocking call similar to a thread abort and stop the execution. An exception is cached ready for when all the other threads (concurrent iterations) eventually finish and return, the exception is thrown in the initiating thread (where the loop is declared).

I chose not to use the Cancel option on a CancellationTokenSource object. This is wasteful and arguably violates the well known anti-patten of controlling the flow of the code by Exceptions.

Instead, it is arguably 'better' to implement a simple thread safe property i.e. Bool stopExecuting. Then within the loop, check the value of stopExecuting and if the value is set to true by the external influence, we can take an alternate path to close down gracefully. Since we should not call cancel, this precludes checking CancellationTokenSource.IsCancellationRequested which would otherwise be another option.

Something like the following if condition would be appropriate within the loop;

if (loopState.ShouldExitCurrentIteration || loopState.IsExceptional || stopExecuting) {loopState.Stop(); return;}

The iteration will now exit in a 'controlled' manner as well as terminating further iterations, but as I said, this does little for our issue of having to wait on the long running and blocking call(s) that are made within each iteration (parallel loop thread), since these have to complete before each thread can get to the option of checking if it should stop.

In summary, as the user closes the form, the 20 threads will be signaled to stop via stopExecuting, but they will only stop when they have finished executing their long running function call.

We can't do anything about the fact that the application domain will always stay alive and only be released when all foreground threads have completed. And this means there will be a delay associated with waiting for any blocking calls made within the loop to complete.

Only a true thread abort can interrupt the blocking call, and you must mitigate leaving the system in a unstable/undefined state the best you can in the aborted thread's exception handler which goes without question. Whether that's appropriate is a matter for the programmer to decide, based on what resource handles they chose to maintain and how easy it is to close them in a thread's finally block. You could register with a token to terminate on cancel as a semi workaround i.e.

CancellationTokenSource cts = new CancellationTokenSource();
ParallelOptions po = new ParallelOptions();
po.CancellationToken = cts.Token;
po.MaxDegreeOfParallelism = System.Environment.ProcessorCount;

Parallel.ForEach(iListOfItems, po, (item, loopState) =>
{

    using (cts.Token.Register(Thread.CurrentThread.Abort))
    {
        Try
        {
           Thread.Sleep(120000); // pretend web service call          
        }
        Catch(ThreadAbortException ex)
        {
           // log etc.
        }
        Finally
        {
          // clean up here
        }
    }

});

but this will still result in an exception in the declaring thread.

All things considered, interrupt blocking calls using the parallel.loop constructs could have been a method on the options, avoiding the use of more obscure parts of the library. But why there is no option to cancel and avoid throwing an exception in the declaring method strikes me as a possible oversight.

川水往事 2024-10-12 18:49:47

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

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

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 example code

But can I abort a Task (in .Net 4.0) in the same way not by
cancellation mechanism. I want to kill the Task immediately.

Other answerers have told you not to do it. But yes, you can do it. You can supply Thread.Abort() as the delegate to be called by the Task's cancellation mechanism. Here is how you could configure this:

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();
  }

}

disclaimer: don't do this.

Here is an example of what not to do:

 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

陌上青苔 2024-10-12 18:49:47
  1. 您不应该使用 Thread.Abort()
  2. 任务可以取消但不能中止。

Thread.Abort() 方法已(严重)弃用。

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

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


这个答案是关于 .net 3.5 及更早版本的。

从那时起,通过改变finally 块的工作方式,线程中止处理得到了改进。

但 Thread.Abort 仍然是一个值得怀疑的解决方案,您应该始终尽量避免。


在 .net Core (.net 5+) 中,Thread.Abort() 现在将抛出 PlatformNotSupportedException

有点强调“已弃用”的观点。

  1. You shouldn't use Thread.Abort()
  2. Tasks can be Cancelled but not aborted.

The Thread.Abort() method is (severely) deprecated.

Both Threads and Tasks should cooperate when being stopped, otherwise you run the risk of leaving the system in a unstable/undefined state.

If you do need to run a Process and kill it from the outside, the only safe option is to run it in a separate AppDomain.


This answer is about .net 3.5 and earlier.

Thread-abort handling has been improved since then, a.o. by changing the way finally blocks work.

But Thread.Abort is still a suspect solution that you should always try to avoid.


And in .net Core (.net 5+) Thread.Abort() will now throw a PlatformNotSupportedException .

Kind of underscoring the 'deprecated' point.

压抑⊿情绪 2024-10-12 18:49:47

每个人都知道(希望)终止线程是不好的。问题是当您不拥有您正在调用的一段代码时。如果这段代码在某些 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);

Everyone knows (hopefully) its bad to terminate thread. The problem is when you don't own a piece of code you're calling. If this code is running in some do/while infinite loop , itself calling some native functions, etc. you're basically stuck. When this happens in your own code termination, stop or Dispose call, it's kinda ok to start shooting the bad guys (so you don't become a bad guy yourself).

So, for what it's worth, I've written those two blocking functions that use their own native thread, not a thread from the pool or some thread created by the CLR. They will stop the thread if a timeout occurs:

// 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);
等风来 2024-10-12 18:49:47

虽然可以中止线程,但实际上这样做几乎总是一个非常糟糕的主意。中止线程意味着线程没有机会自行清理,从而使资源未被删除,并且事物处于未知状态。

实际上,如果中止线程,则应该仅在终止进程的同时执行此操作。可悲的是,太多人认为 ThreadAbort 是停止某些事情并继续执行的可行方法,但事实并非如此。

由于任务作为线程运行,因此您可以对它们调用 ThreadAbort,但与通用线程一样,您几乎不想这样做,除非作为最后的手段。

While it's possible to abort a thread, in practice it's almost always a very bad idea to do so. Aborthing a thread means the thread is not given a chance to clean up after itself, leaving resources undeleted, and things in unknown states.

In practice, if you abort a thread, you should only do so in conjunction with killing the process. Sadly, all too many people think ThreadAbort is a viable way of stopping something and continuing on, it's not.

Since Tasks run as threads, you can call ThreadAbort on them, but as with generic threads you almost never want to do this, except as a last resort.

半透明的墙 2024-10-12 18:49:47

您可以通过在您控制的线程上运行任务并中止该线程来“中止”任务。这会导致任务在错误状态下完成,并出现 ThreadAbortException 。您可以使用自定义任务计划程序控制线程创建,如此答案中所述。请注意有关中止线程的警告 适用。

(如果您不确保该任务是在其自己的线程上创建的,则中止该任务将中止线程池线程或启动该任务的线程,而您通常不想这样做。)

You can "abort" a task by running it on a thread you control and aborting that thread. This causes the task to complete in a faulted state with a ThreadAbortException. You can control thread creation with a custom task scheduler, as described in this answer. Note that the caveat about aborting a thread applies.

(If you don't ensure the task is created on its own thread, aborting it would abort either a thread-pool thread or the thread initiating the task, neither of which you typically want to do.)

ぺ禁宫浮华殁 2024-10-12 18:49:47

我在 Excel 的 Application.Workbooks 中遇到了类似的问题。

如果应用程序繁忙,该方法将永远挂起。我的方法只是尝试将其放入任务中并等待,如果花费太长时间,我就将任务保留并离开(没有坏处“在这种情况下”,Excel 会在用户完成忙碌的事情时解冻)。

在这种情况下,不可能使用取消令牌。优点是我不需要过多的代码、中止线程等。

public static List<Workbook> GetAllOpenWorkbooks()
{
    //gets all open Excel applications
    List<Application> applications = GetAllOpenApplications();

    //this is what we want to get from the third party library that may freeze
    List<Workbook> books = null;

    //as Excel may freeze here due to being busy, we try to get the workbooks asynchronously
    Task task = Task.Run(() =>
    {
        try 
        { 
            books = applications
                .SelectMany(app => app.Workbooks.OfType<Workbook>()).ToList();
        }
        catch { }
    });
    
    //wait for task completion
    task.Wait(5000);
    return books; //handle outside if books is null
}

I faced a similar problem with Excel's Application.Workbooks.

If the application is busy, the method hangs eternally. My approach was simply to try to get it in a task and wait, if it takes too long, I just leave the task be and go away (there is no harm "in this case", Excel will unfreeze the moment the user finishes whatever is busy).

In this case, it's impossible to use a cancellation token. The advantage is that I don't need excessive code, aborting threads, etc.

public static List<Workbook> GetAllOpenWorkbooks()
{
    //gets all open Excel applications
    List<Application> applications = GetAllOpenApplications();

    //this is what we want to get from the third party library that may freeze
    List<Workbook> books = null;

    //as Excel may freeze here due to being busy, we try to get the workbooks asynchronously
    Task task = Task.Run(() =>
    {
        try 
        { 
            books = applications
                .SelectMany(app => app.Workbooks.OfType<Workbook>()).ToList();
        }
        catch { }
    });
    
    //wait for task completion
    task.Wait(5000);
    return books; //handle outside if books is null
}
允世 2024-10-12 18:49:47

这是我对 @Simon-Mourier 提出的想法的实现,使用 dotnet 线程,代码简短:

    public static bool RunWithAbort(this Action action, int milliseconds)
    {
         if (action == null) throw new ArgumentNullException(nameof(action));

        var success = false;
        var thread = new Thread(() =>
        {
            action();
            success = true;
        });
        thread.IsBackground = true;
        thread.Start();
        thread.Join(milliseconds);          
        thread.Abort();

        return success;
    }

This is my implementation of an idea presented by @Simon-Mourier, using the dotnet thread, short and simple code:

    public static bool RunWithAbort(this Action action, int milliseconds)
    {
         if (action == null) throw new ArgumentNullException(nameof(action));

        var success = false;
        var thread = new Thread(() =>
        {
            action();
            success = true;
        });
        thread.IsBackground = true;
        thread.Start();
        thread.Join(milliseconds);          
        thread.Abort();

        return success;
    }
单挑你×的.吻 2024-10-12 18:49:47
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

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();
});

This is a simple snippet to abort a never-ending task with CancellationTokenSource.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文