C# 多线程编程全面教程
可以让多个方法同时运行
1.线程里如何放方法
2.启动线程: thread1.Start();
3.等待线程结束: thread1.Join();
Thread.Sleep休眠
休眠的用处1:在while(true)循环中加上Sleep(2)休眠2ms,可以大大缓解cpu资源
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace C_之多线程
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
/// <summary>
/// 多线程:Thread
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
// 多线程
Thread thread = new Thread(Fun1);
// 启动线程
thread.Start();
// 多线程
Thread thread2 = new Thread(Fun2);
// 启动线程
thread2.Start();
// 等待线程结束
thread.Join();
thread2.Join();
// 必须在这个两个方法执行之后,再执行
MessageBox.Show("多线程");
}
public void Fun1()
{
for (int i = 0; i < 5; i++)
{
// 睡眠 1s
Thread.Sleep(1000);
Console.WriteLine("第一个函数");
}
}
public void Fun2()
{
for (int i = 0; i < 5; i++)
{
// 睡眠 1s
Thread.Sleep(1000);
Console.WriteLine("第二个函数");
}
}
}
}
多线程编程是现代C#开发中的核心技能之一,它允许开发者充分利用多核处理器的性能优势,创建响应迅速且高效的应用程序。本教程将从基础到高级,全面介绍C#中的多线程编程技术。
一、多线程基础概念
1.1 为什么需要多线程?
- 提高性能:利用多核CPU并行执行任务
- 响应性:保持UI界面流畅,后台执行耗时操作
- 资源利用率:同时处理I/O密集型和CPU密集型任务
- 模块化:将复杂任务分解为独立执行的单元
1.2 线程基础术语
- 线程(Thread):操作系统执行程序的最小单位
- 进程(Process):程序在内存中的运行实例
- 并发(Concurrency):多个任务交替执行(单核CPU)
- 并行(Parallelism):多个任务同时执行(多核CPU)
- 同步(Synchronization):协调线程执行顺序
- 异步(Asynchronous):非阻塞的执行方式
二、C#多线程基础实现
2.1 Thread类(传统方式)
using System;
using System.Threading;
class ThreadExample
{
static void Main()
{
// 创建并启动线程
Thread thread1 = new Thread(DoWork);
thread1.Name = "WorkerThread1";
thread1.Start(); // 启动线程
// 带参数的线程
Thread thread2 = new Thread(() => PrintMessage("Hello from Thread 2!"));
thread2.Start();
// 主线程继续执行
Console.WriteLine($"Main thread {Thread.CurrentThread.ManagedThreadId} working...");
// 等待线程完成(不推荐在实际应用中使用,会阻塞主线程)
thread1.Join();
thread2.Join();
Console.WriteLine("All threads completed.");
}
static void DoWork()
{
Console.WriteLine($"Thread {Thread.CurrentThread.Name} started on {Thread.CurrentThread.ManagedThreadId}");
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"Working... {i}");
Thread.Sleep(500); // 模拟工作
}
Console.WriteLine($"Thread {Thread.CurrentThread.Name} completed.");
}
static void PrintMessage(string message)
{
Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId}: {message}");
}
}
2.2 ThreadPool(线程池)
using System;
using System.Threading;
class ThreadPoolExample
{
static void Main()
{
// 排队执行工作项
ThreadPool.QueueUserWorkItem(DoBackgroundWork, "Task 1");
ThreadPool.QueueUserWorkItem(DoBackgroundWork, "Task 2");
// 使用lambda表达式
ThreadPool.QueueUserWorkItem(state =>
{
Console.WriteLine($"Lambda task: {state}");
Thread.Sleep(1000);
}, "Lambda Task");
Console.WriteLine("Main thread continuing...");
// 等待线程池完成(实际中通常不需要,线程池是长期运行的)
Thread.Sleep(3000);
}
static void DoBackgroundWork(object state)
{
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} processing {state}");
Thread.Sleep(500); // 模拟工作
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} completed {state}");
}
}
三、Task Parallel Library (TPL)
3.1 Task类(推荐方式)
using System;
using System.Threading.Tasks;
class TaskExample
{
static async Task Main()
{
Console.WriteLine($"Main thread started on {Thread.CurrentThread.ManagedThreadId}");
// 创建并启动Task
Task task1 = Task.Run(() =>
{
Console.WriteLine($"Task 1 started on {Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(1000);
Console.WriteLine($"Task 1 completed on {Thread.CurrentThread.ManagedThreadId}");
return "Result from Task 1";
});
// 带参数的Task
Task<string> task2 = Task.Run(() => ProcessData("Data for Task 2"));
// 等待所有Task完成并获取结果
string[] results = await Task.WhenAll(task1, task2);
Console.WriteLine("All tasks completed:");
foreach (var result in results)
{
Console.WriteLine(result);
}
// 继续执行主线程
Console.WriteLine($"Main thread continuing on {Thread.CurrentThread.ManagedThreadId}");
}
static string ProcessData(string input)
{
Console.WriteLine($"Processing {input} on {Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(1500);
return $"Processed: {input}";
}
}
3.2 Parallel类(并行循环)
using System;
using System.Threading.Tasks;
class ParallelExample
{
static void Main()
{
Console.WriteLine($"Main thread {Thread.CurrentThread.ManagedThreadId}");
// 并行For循环
Parallel.For(0, 10, i =>
{
Console.WriteLine($"Parallel.For: Processing {i} on {Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(100); // 模拟工作
});
Console.WriteLine("Parallel.For completed");
// 并行ForEach循环
string[] data = { "Apple", "Banana", "Cherry", "Date" };
Parallel.ForEach(data, item =>
{
Console.WriteLine($"Parallel.ForEach: Processing {item} on {Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(200); // 模拟工作
});
Console.WriteLine("Parallel.ForEach completed");
// 带取消支持的并行循环
var cts = new CancellationTokenSource();
cts.CancelAfter(1000); // 1秒后取消
try
{
Parallel.For(0, 20, new ParallelOptions
{
CancellationToken = cts.Token,
MaxDegreeOfParallelism = 2 // 限制最大并行度
}, i =>
{
Console.WriteLine($"Cancellable loop: {i}");
Thread.Sleep(300); // 模拟工作
});
}
catch (OperationCanceledException)
{
Console.WriteLine("Loop was cancelled");
}
}
}
四、线程同步与通信
4.1 锁机制(Lock)
using System;
using System.Threading;
class LockExample
{
private static readonly object _lockObj = new object();
private static int _counter = 0;
static void Main()
{
var threads = new Thread[5];
for (int i = 0; i < threads.Length; i++)
{
threads[i] = new Thread(IncrementCounter);
threads[i].Start();
}
foreach (var thread in threads)
{
thread.Join();
}
Console.WriteLine($"Final counter value: {_counter}");
}
static void IncrementCounter()
{
for (int i = 0; i < 10000; i++)
{
// 使用lock确保线程安全
lock (_lockObj)
{
_counter++;
}
}
}
}
4.2 Monitor类(更灵活的锁)
using System;
using System.Threading;
class MonitorExample
{
private static readonly object _lockObj = new object();
private static bool _isReady = false;
static void Main()
{
var worker = new Thread(WorkerThread);
worker.Start();
// 主线程准备数据
Thread.Sleep(2000); // 模拟准备时间
lock (_lockObj)
{
_isReady = true;
Monitor.Pulse(_lockObj); // 通知等待的线程
}
worker.Join();
}
static void WorkerThread()
{
lock (_lockObj)
{
while (!_isReady)
{
Console.WriteLine("Worker waiting for data...");
Monitor.Wait(_lockObj); // 释放锁并等待
}
Console.WriteLine("Worker received notification, processing data...");
}
}
}
4.3 Mutex(跨进程同步)
using System;
using System.Threading;
class MutexExample
{
private static Mutex _mutex = new Mutex(false, "Global\\MyAppMutex");
static void Main()
{
Console.WriteLine("Attempting to acquire mutex...");
// 尝试获取互斥体
if (_mutex.WaitOne(TimeSpan.FromSeconds(5), false))
{
try
{
Console.WriteLine("Mutex acquired, performing critical operation...");
Thread.Sleep(3000); // 模拟关键操作
}
finally
{
_mutex.ReleaseMutex();
Console.WriteLine("Mutex released");
}
}
else
{
Console.WriteLine("Could not acquire mutex, another instance may be running");
}
}
}
4.4 Semaphore(信号量)
using System;
using System.Threading;
class SemaphoreExample
{
private static Semaphore _semaphore = new Semaphore(3, 3); // 初始3,最大3
static void Main()
{
for (int i = 0; i < 5; i++)
{
new Thread(Worker).Start(i);
}
Console.ReadLine(); // 保持主线程运行
}
static void Worker(object id)
{
Console.WriteLine($"Worker {id} waiting for semaphore...");
_semaphore.WaitOne(); // 获取信号量
Console.WriteLine($"Worker {id} entered the critical section");
Thread.Sleep(1000); // 模拟工作
Console.WriteLine($"Worker {id} leaving the critical section");
_semaphore.Release(); // 释放信号量
}
}
4.5 事件等待句柄(EventWaitHandle)
using System;
using System.Threading;
class EventWaitHandleExample
{
private static EventWaitHandle _waitHandle = new AutoResetEvent(false);
static void Main()
{
var worker = new Thread(WorkerThread);
worker.Start();
Console.WriteLine("Main thread working...");
Thread.Sleep(3000); // 模拟工作
Console.WriteLine("Main thread signaling worker...");
_waitHandle.Set(); // 触发事件
worker.Join();
}
static void WorkerThread()
{
Console.WriteLine("Worker thread waiting for signal...");
_waitHandle.WaitOne(); // 等待事件
Console.WriteLine("Worker thread received signal, performing task...");
Thread.Sleep(2000); // 模拟工作
}
}
五、高级多线程模式
5.1 生产者-消费者模式
using System;
using System.Collections.Concurrent;
using System.Threading;
class ProducerConsumerExample
{
private static BlockingCollection<int> _queue = new BlockingCollection<int>(100);
static void Main()
{
var producerThread = new Thread(Producer);
var consumerThread = new Thread(Consumer);
producerThread.Start();
consumerThread.Start();
producerThread.Join();
consumerThread.Join();
}
static void Producer()
{
Random random = new Random();
for (int i = 0; i < 20; i++)
{
int item = random.Next(100);
_queue.Add(item);
Console.WriteLine($"Produced: {item}");
Thread.Sleep(random.Next(100, 500)); // 模拟生产时间
}
_queue.CompleteAdding(); // 标记生产完成
}
static void Consumer()
{
foreach (var item in _queue.GetConsumingEnumerable())
{
Console.WriteLine($"Consumed: {item}");
Thread.Sleep(800); // 模拟消费时间
}
Console.WriteLine("Consumer finished");
}
}
5.2 线程本地存储(ThreadLocal)
using System;
using System.Threading;
class ThreadLocalExample
{
private static ThreadLocal<int> _threadLocalCounter = new ThreadLocal<int>(() =>
{
Random random = new Random();
return random.Next(1000, 9999); // 每个线程有自己的随机种子
});
static void Main()
{
var threads = new Thread[3];
for (int i = 0; i < threads.Length; i++)
{
threads[i] = new Thread(Worker);
threads[i].Start();
}
foreach (var thread in threads)
{
thread.Join();
}
}
static void Worker()
{
int counter = _threadLocalCounter.Value;
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} counter: {counter}");
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId}: {counter} * {i} = {counter * i}");
Thread.Sleep(100);
}
}
}
5.3 异步编程模式(async/await)
using System;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
class AsyncExample
{
static async Task Main()
{
Console.WriteLine($"Main thread started on {Thread.CurrentThread.ManagedThreadId}");
// 并行下载多个URL
string[] urls = {
"https://example.com",
"https://microsoft.com",
"https://github.com"
};
Task<string>[] downloadTasks = urls.Select(url => DownloadPageAsync(url)).ToArray();
string[] results = await Task.WhenAll(downloadTasks);
Console.WriteLine("All downloads completed:");
foreach (var result in results)
{
Console.WriteLine($"Downloaded {result.Length} bytes");
}
Console.WriteLine($"Main thread continuing on {Thread.CurrentThread.ManagedThreadId}");
}
static async Task<string> DownloadPageAsync(string url)
{
using (HttpClient client = new HttpClient())
{
Console.WriteLine($"Starting download from {url} on {Thread.CurrentThread.ManagedThreadId}");
string content = await client.GetStringAsync(url);
Console.WriteLine($"Completed download from {url} on {Thread.CurrentThread.ManagedThreadId}");
return content;
}
}
}
六、多线程最佳实践
6.1 设计原则
- 避免共享状态:尽可能减少线程间共享的数据
- 最小化临界区:锁定的代码块应尽可能小
- 避免死锁:保持锁获取顺序一致,使用超时
- 考虑线程亲和性:某些操作可能需要特定线程执行(如UI线程)
- 使用线程安全集合:如
ConcurrentQueue、ConcurrentDictionary等
6.2 性能考虑
- 线程池使用:对于短期任务,优先使用线程池
- 并行度控制:根据CPU核心数调整并行度
- I/O密集型 vs CPU密集型:不同类型任务需要不同策略
- 任务取消:提供优雅的取消机制
- 异常处理:确保线程中的异常被正确捕获和处理
6.3 常见陷阱
- 竞态条件:多个线程同时修改共享数据
- 死锁:两个或多个线程互相等待对方释放资源
- 活锁:线程不断重试但无法前进
- 饥饿:某些线程无法获得执行机会
- 线程泄漏:未正确终止的线程
6.4 调试技巧
- 使用Visual Studio调试器:并行堆栈窗口、并行任务窗口
- 日志记录:记录线程ID和操作
- 最小化测试:隔离问题线程
- 使用性能分析器:识别线程瓶颈
七、完整示例:并行文件处理
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Threading.Tasks;
class ParallelFileProcessor
{
static async Task Main()
{
string directory = @"C:\Temp\Files"; // 替换为实际目录
string outputFile = @"C:\Temp\Results\summary.txt";
try
{
Console.WriteLine("Starting parallel file processing...");
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
// 获取所有文件
string[] files = Directory.GetFiles(directory, "*.txt");
Console.WriteLine($"Found {files.Length} files to process");
// 并行处理文件
var results = new ConcurrentBag<string>();
ParallelOptions options = new ParallelOptions
{
MaxDegreeOfParallelism = Environment.ProcessorCount * 2, // 限制并行度
CancellationToken = new CancellationTokenSource(TimeSpan.FromMinutes(5)).Token // 5分钟超时
};
await Task.Run(() =>
{
Parallel.ForEach(files, options, file =>
{
try
{
string content = File.ReadAllText(file);
int wordCount = CountWords(content);
string result = $"File: {Path.GetFileName(file)}, Words: {wordCount}";
results.Add(result);
Console.WriteLine(result);
}
catch (Exception ex)
{
Console.WriteLine($"Error processing {file}: {ex.Message}");
}
});
});
// 保存结果
Directory.CreateDirectory(Path.GetDirectoryName(outputFile));
File.WriteAllLines(outputFile, results);
stopwatch.Stop();
Console.WriteLine($"Processing completed in {stopwatch.ElapsedMilliseconds}ms");
Console.WriteLine($"Results saved to {outputFile}");
}
catch (OperationCanceledException)
{
Console.WriteLine("Processing was cancelled due to timeout");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
static int CountWords(string text)
{
// 简单单词计数(实际可能需要更复杂的逻辑)
return text.Split(new[] { ' ', '\t', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries).Length;
}
}
八、总结
C#提供了丰富的多线程编程工具,从传统的Thread类到现代的Task Parallel Library和async/await模式。关键要点:
-
选择正确的工具:
- 简单后台任务:
ThreadPool或Task.Run - 并行循环:
Parallel.For/Parallel.ForEach - 异步I/O:
async/await - 复杂同步需求:
Monitor/Mutex/Semaphore
- 简单后台任务:
-
同步策略:
- 优先使用不可变数据
- 必须共享时使用线程安全集合
- 临界区尽可能小
- 考虑使用
lock或Interlocked类
-
现代C#多线程:
async/await是异步编程的首选方式ValueTask可以减少分配(高频率场景)Channel提供线程安全的生产者-消费者队列
-
调试与优化:
- 使用性能分析器识别瓶颈
- 监控线程创建和上下文切换
- 考虑使用
ParallelOptions调整并行度
通过合理使用这些技术,你可以构建出高效、响应迅速且线程安全的C#应用程序。多线程编程虽然复杂,但掌握这些模式和最佳实践将使你能够充分利用现代硬件的能力。
1050

被折叠的 条评论
为什么被折叠?



