目录
[使用 ThreadStart 委托](#使用 ThreadStart 委托)
[使用 ParameterizedThreadStart 委托](#使用 ParameterizedThreadStart 委托)
[使用 ThreadPool 的一般步骤](#使用 ThreadPool 的一般步骤)
[Sleep(int millisecondsTimeout)](#Sleep(int millisecondsTimeout))
SetApartmentState(ApartmentState)
在C#中,线程(Thread)是执行代码的基本单位。一个程序可以包含多个线程,这些线程可以并行执行,从而提高效率和响应能力。
什么是线程?
线程是操作系统中执行代码的最小单位,它是进程(Process)内的一个独立执行流。每个线程都有自己的调用堆栈和程序计数器,但与同一进程内的其他线程共享内存和资源。使用线程可以实现多任务处理,提高程序的响应速度。
Thread类的定义
Thread
类位于 System.Threading
命名空间中。每个 Thread
对象表示一个线程,可以通过该对象启动、停止和管理线程的执行。
创建和启动线程
创建线程的一般步骤如下:
- 创建Thread对象:指定线程要执行的方法。
- 启动线程 :调用
Start()
方法。 - 可选:等待线程结束 :使用
Join()
方法。
需要创建一个线程。可以通过传递一个ThreadStart
或ParameterizedThreadStart
委托来指定线程要执行的方法。
使用 ThreadStart
委托
这是最常见的方式之一,适用于没有参数的方法。
cs
using System;
using System.Threading;
class Program
{
static void Main()
{
Thread thread = new Thread(PrintNumbers);
thread.Start(); // 启动线程
thread.Join(); // 等待线程完成
}
static void PrintNumbers()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine(i);
Thread.Sleep(100);
}
}
}
使用 ParameterizedThreadStart
委托
如果你需要向线程传递参数,可以使用 ParameterizedThreadStart
委托:
cs
Thread thread = new Thread(new ParameterizedThreadStart(ProcessData));
thread.Start(42); // 传递参数
static void ProcessData(object data)
{
Console.WriteLine($"处理数据: {data}");
}
Lambda简写
以上是原理方法,平时我们可以直接用Lambda表达式来简写
推荐直接使用第三种,不用考虑那么多
cs
// 创建线程,不传递参数----简写ThreadStart 委托
Thread thread = new Thread(() => PrintNumbers());
thread.Start(); // 启动线程
thread.Join(); // 等待线程完成
// 创建一个线程,传递参数----简写ParameterizedThreadStart 委托
Thread thread = new Thread(param => Compute((int)param));
thread.Start(42); // 启动线程并传递参数42
thread.Join(); // 等待线程完成
//直接用的匿名方法,方法内去调用compute,也相当于ThreadStart委托
Thread thread = new Thread(() => Compute(42)); // 直接传递参数42
thread.Start(); // 启动线程
thread.Join(); // 等待线程完成
使用线程池(ThreadPool)
线程池是一个可以重用的线程集合 ,允许程序在需要时获取线程,而不必每次都创建和销毁线程。这样可以提高性能与资源利用率,特别是在需要频繁启动和结束线程的场景中。
使用线程池的优点
- 性能提升:由于线程可以被重用,减少了线程创建和销毁的开销。
- 资源管理:线程池自动管理线程的数量,系统会根据需求动态调整线程池中的线程数量,以避免线程过多导致系统负担过重。
- 简化代码:通过线程池,可以避免手动管理线程的生命周期。
使用 ThreadPool
的一般步骤
- 使用
QueueUserWorkItem
方法提交工作项 :将要执行的任务定义为一个方法(通常是一个Action
)。 - 单独的工作线程将从线程池中获取该任务并执行:无需直接管理线程。
cs
using System;
using System.Threading;
class Program
{
static void Main()
{
Console.WriteLine("启动多个线程...");
// 启动 5 个线程
for (int i = 0; i < 5; i++)
{
int threadId = i; // 捕获循环变量
ThreadPool.QueueUserWorkItem(DoWork, threadId);
}
// 等待用户输入以保持主线程运行
Console.WriteLine("按任意键退出...");
Console.ReadKey();
}
static void DoWork(object state)
{
int id = (int)state;
Console.WriteLine($"线程 {id} 开始工作...");
// 模拟工作负载
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"线程 {id} 正在处理任务 {i + 1}...");
Thread.Sleep(new Random().Next(500, 1000)); // 随机休眠 500 到 1000 毫秒
}
Console.WriteLine($"线程 {id} 完成工作.");
}
}
cs
启动多个线程...
按任意键退出...
线程 0 开始工作...
线程 1 开始工作...
线程 2 开始工作...
线程 3 开始工作...
线程 4 开始工作...
线程 1 正在处理任务 1...
线程 0 正在处理任务 1...
线程 3 正在处理任务 1...
线程 2 正在处理任务 1...
线程 4 正在处理任务 1...
线程 1 正在处理任务 2...
线程 0 正在处理任务 2...
线程 1 完成工作.
线程 2 正在处理任务 2...
线程 3 正在处理任务 2...
线程 4 正在处理任务 2...
...
线程 4 完成工作.
常用方法
Start()
- 描述: 启动线程,并执行与该线程关联的方法。
- 用法 : 在创建
Thread
对象后,调用Start()
方法来启动线程。
Join()
- 阻塞主线程 :当主线程调用
thread.Join()
时,它会被阻塞,直到thread
完成。也就是说,主线程不会继续执行后续的代码,直到这个线程的任务执行完毕。 - 确保执行顺序 :使用
Join
方法的一个常见场景是,当你希望在所有子线程完成之前,确保主线程在做某些事情时,能够保证数据的一致性和正确性。例如,主线程可能需要处理子线程的结果,或者在所有任务完成后再进行下一个逻辑步骤。
cs
using System;
using System.Threading;
class Program
{
static void Main()
{
Thread thread1 = new Thread(() => Compute(1));
Thread thread2 = new Thread(() => Compute(2));
thread1.Start(); // 启动线程1
thread2.Start(); // 启动线程2
// 等待线程完成
thread1.Join(); // 主线程在这里停下来,等待线程1完成
thread2.Join(); // 然后等待线程2完成
Console.WriteLine("所有任务已完成。");
}
static void Compute(int id)
{
Console.WriteLine($"任务{id}正在执行...");
Thread.Sleep(2000); // 模拟一些工作
Console.WriteLine($"任务{id}完成。");
}
}
cs
任务1正在执行...
任务2正在执行...
任务1完成。
任务2完成。
所有任务已完成。
Sleep(int millisecondsTimeout)
- 描述: 使当前线程暂停执行指定的毫秒数。可以用于控制线程的执行频率。
- 用法: 通常在执行任务过程中调用,以便适当地控制进程。
Abort()
- 描述 : 强制终止线程的执行。需要注意的是,
Abort
方法是一个被认为是不安全和不推荐使用的方法。 - 用法: 由于可能导致资源泄漏和不确定的状态,建议使用其他机制(如标志或取消令牌)来停止线程。
cs
using System;
using System.Threading;
class Program
{
static void Main()
{
Thread thread = new Thread(() => {
try
{
while (true) { /* 持续工作 */ }
}
catch (ThreadAbortException)
{
Console.WriteLine("线程被中止。");
}
});
thread.Start();
Thread.Sleep(1000); // 运行一段时间
thread.Abort(); // 请求终止线程
thread.Join(); // 等待线程完成
}
}
//输出: 线程被中止。
IsAlive
- 描述: 获取一个布尔值,指示线程是否仍在运行。可以用于在多线程环境中检查线程状态。
- 用法: 用于判断线程是否处于活动状态。
cs
class Program
{
static void Main()
{
Thread thread = new Thread(() => {
Thread.Sleep(2000); // 模拟长时间任务
});
thread.Start();
Console.WriteLine("Is thread alive? " + thread.IsAlive); // 输出true
thread.Join();
Console.WriteLine("Is thread alive? " + thread.IsAlive); // 输出false
}
}
ManagedThreadId
- 描述: 获取当前线程的唯一标识符。这个ID可以用于跟踪和调试线程。
- 用法: 在需要识别特定线程时,可以使用该属性。
cs
using System;
using System.Threading;
class Program
{
static void Main()
{
Thread thread = new Thread(() => {
Console.WriteLine("当前线程的ManagedThreadId: " + Thread.CurrentThread.ManagedThreadId);
});//当前线程的ManagedThreadId: 5(输出的ID可能会不同,因为它是在运行时生成的)
thread.Start();
thread.Join();
}
}
SetApartmentState(ApartmentState)
- 描述 : 设置线程的单元状态(Apartment State)。可以设置为
STA
(单线程单元)或MTA
(多线程单元)这会影响COM组件的使用。 - 用法 : 在调用
Start()
方法之前设置。
Interrupt()
- 描述 :
Interrupt()
方法用于中断一个正在等待、休眠或阻塞的线程。调用此方法时,如果目标线程处于阻塞状态(如调用Thread.Sleep()
或Thread.Join()
),该线程将抛出ThreadInterruptedException
异常,并可以立即恢复执行。对于任何未阻塞状态下的线程,Interrupt()
方法不会产生影响。 - 用法 : 如果一个线程正在休眠或等待,可以使用
Interrupt()
来唤醒它。
cs
using System;
using System.Threading;
class Program
{
static void Main()
{
// 创建一个线程,模拟一个长时间执行的操作
Thread workerThread = new Thread(() =>
{
try
{
Console.WriteLine("线程正在休眠...");
Thread.Sleep(10000); // 睡眠10秒
Console.WriteLine("线程完成工作。");
}
catch (ThreadInterruptedException)
{
Console.WriteLine("线程被中断!");
}
});
// 启动线程
workerThread.Start();
// 主线程等待一段时间后中断子线程
Thread.Sleep(2000); // 等待2秒
Console.WriteLine("请求中断线程...");
workerThread.Interrupt(); // 中断线程
// 等待子线程完成
workerThread.Join();
Console.WriteLine("主线程结束。");
}
}
cs
线程正在休眠...
请求中断线程...
线程被中断!
主线程结束。