01、线程基础
线程基础
1. 启动线程的基本语法
在C#中,启动线程主要通过System.Threading.Thread类实现。线程可以执行无参数或带参数的方法,也可以通过lambda表达式定义要执行的代码。
基本语法
- 启动无参数方法
using System;
using System.Threading;
class ThreadExample
{
static void Main()
{
// 创建线程,指定要执行的方法
Thread thread = new Thread(WorkerMethod);
// 启动线程
thread.Start();
// 主线程继续执行
Console.WriteLine("主线程正在执行");
// 等待子线程完成(可选)
thread.Join();
Console.WriteLine("所有线程执行完成");
}
static void WorkerMethod()
{
Console.WriteLine("子线程开始执行");
// 模拟工作
Thread.Sleep(1000);
Console.WriteLine("子线程执行完成");
}
}
- 启动带参数的方法
using System;
using System.Threading;
class ThreadWithParameterExample
{
static void Main()
{
// 创建线程,指定带参数的方法
Thread thread = new Thread(WorkerWithParameter);
// 启动线程并传递参数
thread.Start("线程参数");
// 等待线程完成
thread.Join();
}
// 参数必须是object类型
static void WorkerWithParameter(object data)
{
string message = data as string;
if (!string.IsNullOrEmpty(message))
{
Console.WriteLine($"接收到参数: {message}");
}
Thread.Sleep(1000);
}
}
- 使用Lambda表达式
using System;
using System.Threading;
class ThreadWithLambda
{
static void Main()
{
// 使用lambda表达式定义线程要执行的代码
Thread thread = new Thread(() =>
{
Console.WriteLine("Lambda线程开始");
Thread.Sleep(1000);
Console.WriteLine("Lambda线程结束");
});
thread.Start();
thread.Join();
}
}
线程的常用属性和方法
Start(): 启动线程Join(): 阻塞调用线程,直到该线程完成Sleep(int millisecondsTimeout): 使线程暂停指定的毫秒数IsAlive: 获取一个值,指示当前线程是否处于活动状态Priority: 设置线程的调度优先级Name: 获取或设置线程的名称
2. 为何使用线程实现分而治之
分而治之(Divide and Conquer)是一种算法设计策略,它将复杂问题分解为 smaller 子问题,并行解决这些子问题,最后合并结果。使用线程实现分而治之有以下优势:
- 提高性能:充分利用多核CPU的计算能力
- 简化设计:每个子问题可以独立处理,逻辑更清晰
- 更好的资源利用:避免单个核心满载而其他核心闲置
分而治之的线程实现案例:数组求和
using System;
using System.Threading;
class ArraySumWithThreads
{
private static int[] _array;
private static long _totalSum;
private static readonly object _lock = new object();
static void Main()
{
// 创建一个大数组
_array = new int[1000000];
Random rand = new Random();
for (int i = 0; i < _array.Length; i++)
{
_array[i] = rand.Next(1, 10);
}
Console.WriteLine("开始计算数组总和...");
DateTime startTime = DateTime.Now;
// 分而治之:将数组分成4个部分,用4个线程计算
int segmentSize = _array.Length / 4;
Thread[] threads = new Thread[4];
for (int i = 0; i < 4; i++)
{
int start = i * segmentSize;
int end = (i == 3) ? _array.Length : start + segmentSize;
threads[i] = new Thread(() => CalculateSum(start, end));
threads[i].Start();
}
// 等待所有线程完成
foreach (var thread in threads)
{
thread.Join();
}
TimeSpan elapsed = DateTime.Now - startTime;
Console.WriteLine($"数组总和: {_totalSum}");
Console.WriteLine($"计算耗时: {elapsed.TotalMilliseconds} 毫秒");
}
static void CalculateSum(int start, int end)
{
long sum = 0;
for (int i = start; i < end; i++)
{
sum += _array[i];
}
// 线程安全地累加结果
lock (_lock)
{
_totalSum += sum;
}
}
}
分而治之的适用场景
- 大型数据集的处理(排序、搜索、统计)
- 数学计算(矩阵运算、数值积分)
- 递归问题(斐波那契数列、快速排序)
- 图像处理(对图像的不同区域进行并行处理)
3. 为何使用线程卸载长时间运行的任务
在应用程序中,长时间运行的任务(如下载大文件、复杂计算、数据库查询)如果在主线程执行,会导致应用程序界面冻结,用户体验下降。使用线程卸载这些任务有以下好处:
- 保持UI响应性:主线程可以继续处理用户输入和界面更新
- 提高用户体验:用户可以进行其他操作或看到进度反馈
- 资源优化:可以在任务执行时释放或重新分配资源
- 可取消性:允许用户取消长时间运行的操作
卸载长时间运行任务的案例:文件下载器
using System;
using System.Threading;
class FileDownloader
{
private bool _isDownloading;
private bool _isCancelled;
private Thread _downloadThread;
public void StartDownload(string url)
{
if (_isDownloading)
{
Console.WriteLine("已经在下载中...");
return;
}
_isDownloading = true;
_isCancelled = false;
Console.WriteLine("开始下载...");
// 在新线程中启动下载
_downloadThread = new Thread(() => DownloadFile(url));
_downloadThread.IsBackground = true; // 设为后台线程,程序退出时自动终止
_downloadThread.Start();
// 主线程可以继续响应用户输入
MonitorDownloadProgress();
}
private void DownloadFile(string url)
{
// 模拟文件下载过程
for (int i = 0; i <= 100; i += 5)
{
if (_isCancelled)
{
Console.WriteLine("\n下载已取消");
_isDownloading = false;
return;
}
// 模拟下载进度
Thread.Sleep(500); // 模拟网络延迟
// 这里应该使用线程安全的方式更新进度
lock (this)
{
Progress = i;
}
}
Console.WriteLine("\n下载完成!");
_isDownloading = false;
}
public int Progress { get; private set; }
public void CancelDownload()
{
_isCancelled = true;
}
private void MonitorDownloadProgress()
{
// 主线程监控下载进度并响应用户输入
while (_isDownloading)
{
Console.Write($"\r当前进度: {Progress}% (按C取消)");
if (Console.KeyAvailable)
{
ConsoleKeyInfo key = Console.ReadKey(true);
if (key.Key == ConsoleKey.C)
{
CancelDownload();
break;
}
}
Thread.Sleep(100);
}
}
static void Main()
{
FileDownloader downloader = new FileDownloader();
downloader.StartDownload("https://example.com/largefile.zip");
}
}
长时间运行任务的常见类型
- 网络操作(文件下载、API调用)
- 数据库操作(复杂查询、批量插入)
- 计算密集型任务(数据分析、建模、图像处理)
- I/O操作(大文件读写、备份)
总结
-
线程启动语法:
- 使用
Thread类创建线程,通过Start()方法启动 - 支持无参数方法、带参数方法和lambda表达式
- 可通过
Join()方法等待线程完成,Sleep()方法暂停线程
- 使用
-
分而治之的线程应用:
- 将大问题分解为小问题,并行处理提高效率
- 充分利用多核CPU,适合数据密集型任务
- 需要注意线程间的数据同步和结果合并
-
长时间运行任务的线程卸载:
- 保持主线程(尤其是UI线程)的响应性
- 提供更好的用户体验和操作灵活性
- 需实现进度报告和取消机制
- 通常使用后台线程避免影响程序退出
线程是实现并行计算的基础,但也带来了复杂性,如同步问题、死锁风险和调试难度。在实际开发中,还可以考虑使用更高级的抽象,如Task、Parallel类等,它们提供了更简洁的编程模型和更好的性能控制。

浙公网安备 33010602011771号