Files
xuyueandClaude Opus 5.5 91b39603a2 项目 5 补齐:旧版线程 API(Suspend / Resume / Abort)与 ListBox
- 编译时把 Thread 的 Suspend / Resume / Abort、ThreadState、Sleep、new Thread 改写成平台的
  协作式实现:被挂起或终止的线程在 Sleep、后台改控件、调设备类库、弹提示框时停下或结束;
  ThreadStateException 等行为与 .NET Framework 一致;编辑器不再提示"已过时"
- ListBox:列表项、单选、SelectedIndexChanged 先于 Click、删除选中项后取消选中;
  ComboBox 与 ListBox 共用 ListControl 和列表项集合;设计器工具箱加"列表框"
- 评分规则的"包含"对列表框检查所有列表项
- 示例:项目 5 任务 2(烟雾,线程)、任务 3(推送灾情,二维数组、两个窗体、ListBox),
  参考答案修正书上直接 Abort 挂起线程、烟雾记成"发现火情"的问题
- 一直不换行的 Console 输出攒满 4096 字符先发出,避免占满内存
- 测试:旧版线程 API、ListBox 事件顺序、两个示例的端到端和评分测试

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 20:33:12 +08:00

166 lines
6.6 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading;
namespace Sim.Host;
/// <summary>
/// 教材项目 5 用到的旧版线程 API:Thread.Suspend / Resume / Abort 在新版 .NET 上会抛出 PlatformNotSupportedException。
/// 编译时把学生代码里对它们(以及 new Thread、Thread.Sleep、ThreadState)的调用改写成这里的方法(见 Sim.Runner/LegacyThreadRewriter.cs)。
/// <para>
/// 新版 .NET 不能从外部强行停住或中止一个线程,所以这里是"协作式"的:被挂起或中止的线程在下一个检查点停下或结束。
/// 检查点是 Thread.Sleep、从后台线程修改控件属性、调用设备类库、弹出提示框,
/// 教材里的线程方法每次循环都会经过它们,看到的效果和 .NET Framework 一致。
/// </para>
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public static class LegacyThreads
{
private sealed class State
{
public volatile bool Suspended;
public volatile bool AbortRequested;
}
// 线程少、操作不频繁,用一把锁保护所有线程的状态;挂起和休眠都在这把锁上等待
private static readonly object Gate = new();
private static readonly ConditionalWeakTable<Thread, State> States = new();
/// <summary>当前线程是学生用 new Thread 创建的线程时,指向它的状态</summary>
[ThreadStatic] private static State? _current;
private static State StateOf(Thread thread) => States.GetValue(thread, static _ => new State());
// ---------- 改写后的调用 ----------
/// <summary>new Thread(GetData) → new Thread(Wrap(new ThreadStart(GetData))):线程被中止时安静地结束</summary>
public static ThreadStart Wrap(ThreadStart start) => () => RunThread(() => start());
public static ParameterizedThreadStart Wrap(ParameterizedThreadStart start) => obj => RunThread(() => start(obj));
private static void RunThread(Action body)
{
var state = StateOf(Thread.CurrentThread);
_current = state;
try
{
// 启动前就被 Abort 的线程(.NET Framework 里启动后立即结束)
CheckPoint(state);
body();
}
catch (ThreadAbortedException)
{
// 和 .NET Framework 一样,被中止的线程结束时不算未处理的异常
}
}
public static void Suspend(Thread thread)
{
lock (Gate)
{
if ((thread.ThreadState & (ThreadState.Unstarted | ThreadState.Stopped)) != 0)
throw new ThreadStateException("线程没有在运行,无法挂起。");
StateOf(thread).Suspended = true;
}
// 线程挂起自己:立即停下,直到别的线程调用 Resume
if (thread == Thread.CurrentThread) CheckPoint(StateOf(thread));
}
public static void Resume(Thread thread)
{
lock (Gate)
{
var state = StateOf(thread);
if (!state.Suspended) throw new ThreadStateException("线程没有被挂起,无法继续(Resume 只能用在调用过 Suspend 的线程上)。");
state.Suspended = false;
Monitor.PulseAll(Gate);
}
}
public static void Abort(Thread thread) => Abort(thread, null);
public static void Abort(Thread thread, object? stateInfo)
{
lock (Gate)
{
if ((thread.ThreadState & ThreadState.Stopped) != 0) return;
var state = StateOf(thread);
state.AbortRequested = true;
// 正在 Thread.Sleep 的线程立即醒来结束
Monitor.PulseAll(Gate);
// .NET Framework 不能直接中止挂起的线程:抛出异常,等它被 Resume 后才结束
if (state.Suspended) throw new ThreadStateException("线程已挂起,不能直接终止。先调用 Resume() 让线程继续,再调用 Abort()。");
}
if (thread == Thread.CurrentThread) throw new ThreadAbortedException();
}
/// <summary>thd.ThreadState:在真实状态上加上挂起、请求中止</summary>
public static ThreadState GetThreadState(Thread thread)
{
var actual = thread.ThreadState;
if (!States.TryGetValue(thread, out var state) || (actual & ThreadState.Stopped) != 0) return actual;
var result = actual & (ThreadState.Background | ThreadState.Unstarted);
if (state.Suspended) result |= ThreadState.Suspended;
else if ((actual & ThreadState.Unstarted) == 0) result |= actual & ThreadState.WaitSleepJoin;
if (state.AbortRequested) result |= ThreadState.AbortRequested;
return result;
}
public static void Sleep(int millisecondsTimeout)
{
var state = _current;
if (state == null)
{
Thread.Sleep(millisecondsTimeout);
return;
}
if (millisecondsTimeout < Timeout.Infinite) throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout), "休眠时间不能是负数(-1 表示一直休眠)。");
CheckPoint(state);
var deadline = Environment.TickCount64 + millisecondsTimeout;
lock (Gate)
{
// 休眠中被 Abort 时提前醒来;被 Suspend 的线程睡完后在检查点停下
while (!state.AbortRequested)
{
if (millisecondsTimeout == Timeout.Infinite)
{
Monitor.Wait(Gate);
continue;
}
var remaining = deadline - Environment.TickCount64;
if (remaining <= 0) break;
Monitor.Wait(Gate, (int)remaining);
}
}
CheckPoint(state);
}
public static void Sleep(TimeSpan timeout) => Sleep((int)timeout.TotalMilliseconds);
// ---------- 检查点 ----------
/// <summary>平台内部使用:学生创建的线程在这里响应 Suspend 和 Abort(其他线程直接返回)</summary>
public static void CheckPoint()
{
var state = _current;
if (state != null) CheckPoint(state);
}
private static void CheckPoint(State state)
{
if (!state.Suspended && !state.AbortRequested) return;
lock (Gate)
{
while (state.Suspended) Monitor.Wait(Gate);
if (state.AbortRequested) throw new ThreadAbortedException();
}
}
}
/// <summary>线程被 Abort 时在线程里抛出(对应 .NET Framework 的 ThreadAbortException)</summary>
public sealed class ThreadAbortedException : Exception
{
public ThreadAbortedException() : base("正在中止线程。") { }
}