c# - C# - 在.NET内核中,中止线程的替代方案?

105 4

我的ASP.NET Core 2.2应用程序必须调用第三方库,这个库很容易将CPU拖动到100% ,我没有访问源码的权限,供应商也无法修复它。

解决这个问题的办法是,将第三方库隔离在.NET Framework 4.x web服务中,如果发现问题,我可以调用Thread.Abort ,之所以便将它隔离在.NET Framework服务而不是.NET Core中,是因为后者不支持Thread.Abort。当前的解决方案虽然不理想,但是,仍可行。甚至知道Thread.Abort都可能导致不稳定(到目前为止)。

我不希望因为性能原因而隔离库,但是目前为止,我还没有找到在.NET核心项目中杀死失控线程(或任务)的方法。

有什么可供我选择的替代方案?

时间: 原作者:

67 5

但是,如果你喜欢使用Thread.Abort方法,那么至少要使用内核来实现它,并使用WIN32 int调用非托管TerminateThread API 。

下面是这样做的一个例子(警告: 几乎没有被测试过)。


using System;


using System.Runtime.InteropServices;


using System.Threading;


using System.Threading.Tasks;



namespace CoreConsole


{


 class Program


 {


 static async Task Main(string[] args)


 {


 try


 {


 using (var longRunningThread = new LongRunningThread(() => Thread.Sleep(5000)))


 {


 await Task.Delay(2500);


 longRunningThread.Abort();


 await longRunningThread.Completion;


 Console.WriteLine("Finished");


 }


 }


 catch (Exception ex)


 {


 Console.WriteLine($"{ex.Message}");


 }


 }


 }



 public class LongRunningThread : IDisposable


 {


 readonly Thread _thread;



 IntPtr _threadHandle = IntPtr.Zero;



 readonly TaskCompletionSource<bool> _threadEndTcs;



 readonly Task _completionTask;



 public Task Completion { get { return _completionTask; } }



 readonly object _lock = new object();



 public LongRunningThread(Action action)


 {


 _threadEndTcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);



 _thread = new Thread(_ =>


 {


 try


 {


 var hCurThread = NativeMethods.GetCurrentThread();


 var hCurProcess = NativeMethods.GetCurrentProcess();


 if (!NativeMethods.DuplicateHandle(


 hCurProcess, hCurThread, hCurProcess, out _threadHandle,


 0, false, NativeMethods.DUPLICATE_SAME_ACCESS))


 {


 throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());


 }



 action();



 _threadEndTcs.TrySetResult(true);


 }


 catch (Exception ex)


 {


 _threadEndTcs.TrySetException(ex);


 }


 });



 async Task waitForThreadEndAsync()


 {


 try


 {


 await _threadEndTcs.Task.ConfigureAwait(false);


 }


 finally


 {


 // we use TaskCreationOptions.RunContinuationsAsynchronously for _threadEndTcs


 // to mitigate possible deadlocks here


 _thread.Join();


 }


 }



 _thread.IsBackground = true;


 _thread.Start();



 _completionTask = waitForThreadEndAsync();


 }



 public void Abort()


 {


 if (Thread.CurrentThread == _thread)


 throw new InvalidOperationException();



 lock (_lock)


 {


 if (!_threadEndTcs.Task.IsCompleted)


 {


 _threadEndTcs.TrySetException(new ThreadTerminatedException());


 if (NativeMethods.TerminateThread(_threadHandle, uint.MaxValue))


 {


 NativeMethods.WaitForSingleObject(_threadHandle, NativeMethods.INFINITE);


 }


 else


 {


 throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());


 }


 }


 }


 }



 public void Dispose()


 {


 if (Thread.CurrentThread == _thread)


 throw new InvalidOperationException();



 lock (_lock)


 {


 try


 {


 if (_thread.IsAlive)


 {


 Abort();


 _thread.Join();


 }


 }


 finally


 {


 GC.SuppressFinalize(this);


 Cleanup();


 }


 }


 }



 ~LongRunningThread()


 {


 Cleanup();


 }



 void Cleanup()


 {


 if (_threadHandle != IntPtr.Zero)


 {


 NativeMethods.CloseHandle(_threadHandle);


 _threadHandle = IntPtr.Zero;


 }


 }


 }



 public class ThreadTerminatedException : SystemException


 {


 public ThreadTerminatedException() : base(nameof(ThreadTerminatedException)) { }


 }



 internal static class NativeMethods


 {


 public const uint DUPLICATE_SAME_ACCESS = 2;


 public const uint INFINITE = uint.MaxValue;



 [DllImport("kernel32.dll")]


 public static extern IntPtr GetCurrentThread();



 [DllImport("kernel32.dll")]


 public static extern IntPtr GetCurrentProcess();



 [DllImport("kernel32.dll", SetLastError = true)]


 public static extern bool CloseHandle(IntPtr handle);



 [DllImport("kernel32.dll", SetLastError = true)]


 public static extern bool DuplicateHandle(IntPtr hSourceProcessHandle,


 IntPtr hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle,


 uint dwDesiredAccess, bool bInheritHandle, uint dwOptions);



 [DllImport("kernel32.dll", SetLastError = true)]


 public static extern bool TerminateThread(IntPtr hThread, uint dwExitCode);



 [DllImport("kernel32.dll", SetLastError = true)]


 public static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);


 }



}



原作者:
126 0

你可以降低Thread.Priority,这在内核3.0中可用,当没有其他进程需要CPU周期时,它仍然使用所有可用的CPU周期,但是系统将响应更快。

原作者:
...