C# 中止线程:此平台不支持线程中止
Posted
技术标签:
【中文标题】C# 中止线程:此平台不支持线程中止【英文标题】:C# Aborting a thread: Thread abort is not supported on this platform 【发布时间】:2021-08-21 20:55:44 【问题描述】:问题
我在 c# 中创建了一个线程(在 windows 10 上),它向服务器请求一些数据:
每次用户进入某个菜单时都应该创建这样一个线程(mainMenuJoinRoomButtonClick()
)
每次用户退出该菜单时都应该关闭它(我们使用roomListBackButtonClick()
退出)
这样做是为了让我们节省资源和防止线程未关闭时发生的错误。 (请看下面的代码)
我尝试使用threadName.Abort()
,但它给了我以下错误:
System.PlatformNotSupportedException: 'Thread abort is not supported on this platform.'
我也试过threadName.Interrupt()
,它会产生这个错误(错误出现在行:Thread.Sleep(3000);
):
System.Threading.ThreadInterruptedException: 'Thread was interrupted from a waiting state.'
这里有什么问题,我该如何关闭线程?
研究完成
上网查了showed没有什么好的方法可以中止一个线程,是这样吗?
我还找到了使用Thread.sleep()
的this 答案(第一个),但这似乎不是一个真正的解决方案,因为它不会释放线程的资源,我不确定如何在我的代码。
代码
namespace TriviaClient
public partial class MainWindow : Window
public const int GET_ROOMS_REQUEST = 92;
public Thread refreshRoomsListThread;
public static NetworkStream clientStream;
public MainWindow()
StartClient();
InitializeComponent();
public void refreshRoomList()
while (true)
this.Dispatcher.Invoke((Action)(() =>
roomsListErrorBox.Text = "";
));
serializeAndSendMessage(GET_ROOMS_REQUEST, "");
Dictionary<string, object> response = receiveAndDeserializeMessage();
if (response.ContainsKey("status") && (string)response["status"] == "1")
this.Dispatcher.Invoke((Action)(() =>
roomsList.Items.Clear();
));
Dictionary<int, string> rooms = new Dictionary<int, string>();
JArray Jrooms = (JArray)response["rooms"];
foreach (JArray room in Jrooms)
rooms.Add((int)room[0], (string)room[1]);
foreach (KeyValuePair<int, string> roomInDict in rooms)
this.Dispatcher.Invoke((Action)(() =>
roomsList.Items.Add(new Label().Content = $"roomInDict.Key: roomInDict.Value");
));
else if (response.ContainsKey("message"))
this.Dispatcher.Invoke((Action)(() =>
roomsListErrorBox.Text = (string)response["message"];
));
Thread.Sleep(3000);
private void roomListBackButtonClick(object sender, RoutedEventArgs e)
refreshRoomsListThread.Abort();
private void mainMenuJoinRoomButtonClick(object sender, RoutedEventArgs e)
roomsListErrorBox.Text = "";
refreshRoomsListThread = new Thread(refreshRoomList);
refreshRoomsListThread.Start();
【问题讨论】:
while (true) ... Thread.Sleep(3000);
- 为什么不使用可以随时停止的计时器?最好是 DispatcherTimer,它还可以消除对 Dispatcher.Invoke 的需求。
Task + CancellationToken 怎么样? -- 你有serializeAndSendMessage
和receiveAndDeserializeMessage()
的异步版本还是可以将它们转换为异步(看起来像I/O + JSON 序列化)?您调用的频率如此之高,以至于这里可能只使用 async/await。
Thread.Abort
绝不是一个好主意 - 特别是如果您关心错误和资源丢失。为什么不使用线程池,当您不再需要线程时(如@mm8 所建议),将您的后台工作传递给“好的,立即退出”。您可能还想阅读async
和await
。使用更现代的结构可能有更好的方法来做您想做的事情。
serializeAndSendMessage
方法是否接受 CancellationToken
作为参数?
【参考方案1】:
您应该将DispatcherTimer
与async
Tick
事件处理程序一起使用,该事件处理程序在Task
中运行长时间运行的操作。
Tick 事件处理程序在 UI 线程中执行,因此您无需使用 Dispatcher。
private readonly DispatcherTimer timer = new DispatcherTimer
Interval = TimeSpan.FromSeconds(3)
;
public MainWindow()
StartClient();
InitializeComponent();
timer.Tick += OnTimerTick;
private void mainMenuJoinRoomButtonClick(object sender, RoutedEventArgs e)
timer.Start();
private void roomListBackButtonClick(object sender, RoutedEventArgs e)
timer.Stop();
private async void OnTimerTick(object sender, EventArgs e)
roomsListErrorBox.Text = "";
Dictionary<string, object> response = await Task.Run(
() =>
serializeAndSendMessage(GET_ROOMS_REQUEST, "");
return receiveAndDeserializeMessage();
);
if (response.ContainsKey("status") && (string)response["status"] == "1")
roomsList.Items.Clear();
// ...
【讨论】:
而且您需要的似乎更多。计时器通常比带有睡眠的循环更合适。【参考方案2】:您可以设置一个标志以最终退出while
循环,而不是尝试强制中止线程:
private bool _run = true;
public void refreshRoomList()
while (_run)
...
private void roomListBackButtonClick(object sender, RoutedEventArgs e)
_run = false;
【讨论】:
你需要Interlocked.Read
和Interlocked.Write
@Charlieface 为什么需要这些? (这个解决方案在没有这些的情况下有效,现在我正在检查给出的第二个答案,看看它是否也有效)
@snatchysquid 因为它可能是内联的,尽管它确实取决于循环内代码的作用
@Charlieface 你能用Interlocked.Read
和Interlocked.Read
展示代码的外观吗?我不熟悉这些,不知道如何使用它
@snatchysquid while (Volatile.Read(ref _run))
和 Volatile.Write(ref _run, false);
【参考方案3】:
要解决此问题,您可以使用布尔值并让线程处于活动状态,这是一个示例:
public bool KeepAlive get; set; = true;
private void YourThread()
while (KeepAlive)
//Do your task here
当你想停止线程时设置 KeepAlive = false;
【讨论】:
以上是关于C# 中止线程:此平台不支持线程中止的主要内容,如果未能解决你的问题,请参考以下文章
C#错误之 System.Threading.ThreadAbortException:正在中止线程
C# - 线程中止异常(Thread Abort Exception)重新抛出自身