gpt4 book ai didi

c# - 在 .NET Core 世界中中止线程的替代方案?

转载 作者:太空狗 更新时间:2023-10-29 20:22:43 26 4
gpt4 key购买 nike

在我的 ASP.NET Core 2.2 应用程序中,我不得不调用一个 3rd 方库,它很容易将 CPU 推到 100% 并基本上挂起机器 - 每月至少发生两次。我无法访问源代码,供应商也不会修复它。

我对这个问题的解决方案是在 .NET Framework 4.x Web 服务中隔离这个第 3 方库,如果我检测到问题,我可以在其中调用 Thread.Abort。将它隔离在 .NET Framework 服务而不是 .NET Core 中的原因是后者不支持 Thread.Abort。当前的解决方案虽然不理想,但有效。即使知道 Thread.Abort 可能会导致不稳定(到目前为止还没有)。

出于性能原因,我宁愿不隔离库。但到目前为止,我还没有找到一种方法来杀死 .NET Core 项目中的失控线程(或任务)。

我有哪些替代方案?

最佳答案

我也同意 the comment在这种情况下,拆除整个过程可能是一个更干净的解决方案。但是,如果您更愿意坚持使用 Thread.Abort 方法,至少使用 .NET Core for Windows 实现它并不困难,使用 Win32 Interop 调用非托管 TerminateThread应用程序接口(interface)。

下面是一个这样做的例子(警告:几乎未经测试)。

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

}

关于c# - 在 .NET Core 世界中中止线程的替代方案?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58191730/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com