目录

线程与进程 (Threads & Processes)

在 C# 开发中,并发编程主要涉及三个概念:进程 (Process)线程 (Thread)任务 (Task)

进程 (Process)

进程是操作系统中正在运行的应用程序的实例。`Process` 类主要用于启动外部程序、打开文件或与操作系统进程交互。

打开系统应用程序

使用 `Process.Start` 可以直接通过命令名称启动系统路径下的应用程序。

using System.Diagnostics;
 
// 按钮事件示例
private void btnOpenNotepad_Click(object sender, EventArgs e)
{
    // 打开记事本
    Process.Start("notepad");
}
 
private void btnOpenCalc_Click(object sender, EventArgs e)
{
    // 打开计算器
    Process.Start("calc");
}
 
private void btnOpenPaint_Click(object sender, EventArgs e)
{
    // 打开画图工具
    Process.Start("mspaint");
}
 
private void btnOpenUrl_Click(object sender, EventArgs e)
{
    // 打开浏览器访问网址
    // 注意:在 .NET Core/5+ 中可能需要设置 UseShellExecute = true
    Process.Start("iexplore", "http://www.baidu.com");
}

打开指定文件或目录

若要打开特定路径的文件(如 .txt, .docx)或文件夹,通常需要使用 `ProcessStartInfo` 来获得更精细的控制。

private void btnOpenFile_Click(object sender, EventArgs e)
{
    // 实例化 Process 对象
    Process process = new Process();
 
    // 设置启动信息
    // 方式 1:分步设置
    // ProcessStartInfo startInfo = new ProcessStartInfo(@"C:\Users\Desktop\6666.txt");
    // process.StartInfo = startInfo;
 
    // 方式 2:直接赋值 (推荐)
    process.StartInfo = new ProcessStartInfo(@"C:\Users\Desktop\6666.txt")
    {
        // 在 .NET Core / .NET 5+ 中,打开非可执行文件通常需要将此设为 true
        UseShellExecute = true 
    };
 
    // 执行打开
    process.Start();
}

多线程 (Thread)

`Thread` 是 C# 中最基础的线程操作类。线程是进程中的执行单元。

Thread 基础案例

using System.Threading;
 
public void StartThreadDemo()
{
    // 使用 Lambda 表达式创建线程
    Thread thread = new Thread(() =>
    {
        // 可以在这里加锁,但在这种简单结构中,锁(lock)主要用于保护共享资源
        // lock (this) 
        // {
            Console.WriteLine("线程开始工作...");
 
            // 模拟耗时操作
            Thread.Sleep(300); // 线程挂起(暂停)300毫秒
 
            Console.WriteLine("线程工作结束。");
        // }
    });
 
    // --- 关键属性设置 ---
 
    // 设置为后台线程
    // true: 主程序关闭时,此线程会被强制立即结束。
    // false (默认): 主程序关闭时,必须等待此线程执行完毕,进程才会真正退出。
    thread.IsBackground = true;
 
    // 启动线程
    thread.Start();
 
    // 阻塞主线程,直到 thread 线程执行完毕 (通常用于等待子线程结果)
    // thread.Join(); 
}

关于 Suspend 和 Resume 代码中提到的 `thread.Suspend()` (暂停) 和 `thread.Resume()` (恢复) 方法在现代 .NET 开发中已被标记为过时 (Obsolete) 且不建议使用。

原因:如果在持有锁或资源时强制暂停线程,极易导致死锁 (Deadlock)。建议使用 `ManualResetEvent` 或 `AutoResetEvent` 等信号量机制来控制线程的暂停与继续。

跨线程 UI 操作

在 WinForms 等桌面应用中,子线程不能直接修改主线程(UI线程)创建的控件(如 TextBox, Label)。

暴力解决方法(仅用于测试,不推荐生产环境):

public Form1()
{
    InitializeComponent();
    // 取消跨线程非法访问检查
    Control.CheckForIllegalCrossThreadCalls = false;
}

推荐解决方法: 使用 `Invoke` 或 `BeginInvoke`。

线程优先级 (Priority)

线程优先级决定了 CPU 分配给该线程的时间片多少。优先级越高,被 CPU 调度的概率越大,但并不保证一定先执行完(取决于操作系统调度)。

Thread t = new Thread(MyMethod);
 
// 优先级枚举 (ThreadPriority)
t.Priority = ThreadPriority.Highest;      // = 4 (最高)
t.Priority = ThreadPriority.AboveNormal;  // = 3
t.Priority = ThreadPriority.Normal;       // = 2 (默认)
t.Priority = ThreadPriority.BelowNormal;  // = 1
t.Priority = ThreadPriority.Lowest;       // = 0 (最低)
 
t.Start();

线程的阻塞 (Blocking)

理解“谁阻塞了谁”是多线程编程的关键。

方法 描述 谁被阻塞? 状态
Thread.Sleep(300) 当前正在执行代码的线程暂停指定时间。 自己 (当前线程) 释放 CPU 时间片,指定时间后重新排队。
thread1.Join() 在线程 A 中调用 `thread1.Join()`。 调用者 (线程 A) 线程 A 暂停执行,直到 `thread1` 的任务全部完成并退出,线程 A 才会继续往下走。

代码对比:

// 场景 1:Sleep (我休息一会)
Console.WriteLine("开始休息");
Thread.Sleep(1000); // 主线程停止1秒
Console.WriteLine("休息结束");
 
// 场景 2:Join (我等你做完)
Thread t = new Thread(() => { 
    Thread.Sleep(5000); // 子线程工作5秒
});
t.Start();
 
Console.WriteLine("等待子线程...");
t.Join(); // 主线程卡在这里不动,直到 t 执行完毕
Console.WriteLine("子线程做完了,我继续。");

多线程任务 (Task)

`Task` 是基于线程池 (`ThreadPool`) 的高级抽象。相比于 `Thread`,`Task` 性能更好(复用线程),API 更丰富(支持返回值、延续任务、取消等),是现代 C# 并发编程的首选。

Task.Run 案例

`Task.Run` 是在线程池上运行 CPU 密集型代码的最快捷方式。

using System.Threading;
using System.Threading.Tasks;
 
public void TaskDemo()
{
    // 启动一个新任务
    Task.Run(() =>
    {
        Console.WriteLine("Task 正在运行...");
 
        // 模拟耗时操作
        Thread.Sleep(300); 
 
        Console.WriteLine("Task 运行结束");
    });
 
    // 注意:Task.Run 是异步的,主线程会继续立即往下执行。
    // 如果需要等待 Task 完成,可以使用 await (在 async 方法中) 或 task.Wait()。
}

Thread 与 Task 的简单对比: