C#线程的各种使用方式详解
一、基本线程创建与启动
1. Thread类
最基本的线程创建方式是使用Thread类:
Thread thread = new Thread(() => {
Console.WriteLine("线程执行中...");
});
thread.Start();
2. Task类
在.NET 4.0引入的Task并行库提供了更高级的线程管理方式:
Task task = new Task(() => {
Console.WriteLine("使用Task执行异步操作");
});
task.Start();
二、Task的多种创建方式
1. Task构造函数
Task task = new Task(() => {
Console.WriteLine("使用Task执行异步操作");
for (int i = 0; i < 10; i++)
{
Console.WriteLine(i);
}
});
task.Start();
2. Task.Run()
Task.Run()是创建并启动任务的快捷方式:
Task.Run(() => TaskMethod("Task 3"));
3. Task.Factory.StartNew()
提供更多选项的任务创建方式:
Task.Factory.StartNew(() => TaskMethod("Task 4"));
// 标记为长时间运行任务
Task.Factory.StartNew(() => TaskMethod("Task 5"), TaskCreationOptions.LongRunning);
三、带参数和返回值的任务
Task<int> task = new Task<int>(() => {
int sum = 0;
for (int i = 0; i < 100; i++)
{
sum += i;
}
return sum;
});
task.Start();
int result = task.Result; // 阻塞等待结果
四、async/await模式
async/await是C# 5.0引入的异步编程模型,使异步代码更易于编写和理解:
async static Task<int> AsyncGetsum()
{
await Task.Delay(1);
int sum = 0;
Console.WriteLine("使用Task执行异步操作.");
for (int i = 0; i < 100; i++)
{
sum += i;
}
return sum;
}
// 调用方式
var ret1 = AsyncGetsum();
Console.WriteLine("主线程执行其他处理");
int result2 = ret1.Result; // 阻塞主线程
五、任务的取消
使用CancellationTokenSource可以取消正在执行的任务:
var cts = new CancellationTokenSource();
var longTask = new Task<int>(() => TaskMethod("Task 2", 10, cts.Token), cts.Token);
longTask.Start();
// 取消任务
cts.Cancel();
// 任务中检查取消状态
private static int TaskMethod(string name, int seconds, CancellationToken token)
{
for (int i = 0; i < seconds; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(1));
if (token.IsCancellationRequested) return -1;
}
return 42 * seconds;
}
六、任务中的异常处理
1. 单个任务的异常处理
try
{
Task<int> task = Task.Run(() => TaskMethod("Task 2", 2));
int result = task.GetAwaiter().GetResult();
Console.WriteLine("Result: {0}", result);
}
catch (Exception ex)
{
Console.WriteLine("Task 2 Exception caught: {0}", ex.Message);
}
2. 多个任务的异常处理
try
{
var t1 = new Task<int>(() => TaskMethod("Task 3", 3));
var t2 = new Task<int>(() => TaskMethod("Task 4", 2));
t1.Start();
t2.Start();
Task.WaitAll(t1, t2);
}
catch (AggregateException ex)
{
ex.Handle(exception =>
{
Console.WriteLine(exception.Message);
return true;
});
}
3. async/await中的异常处理
static async Task ObserveOneExceptionAsync()
{
var task1 = ThrowNotImplementedExceptionAsync();
var task2 = ThrowInvalidOperationExceptionAsync();
var task3 = Normal();
try
{
Task allTasks = Task.WhenAll(task3, task2, task1);
await allTasks;
}
catch (InvalidOperationException ex)
{
Console.WriteLine("task2 任务报错!");
}
catch (NotImplementedException ex)
{
Console.WriteLine("task1 任务报错!");
}
}
七、Task.FromResult的应用
Task.FromResult用于创建已完成的任务,适用于缓存等场景:
private static Task<string> GetValueFromCache(string key)
{
string result = string.Empty;
if (cache.TryGetValue(key, out result))
{
return Task.FromResult(result);
}
return Task.FromResult("");
}
八、使用IProgress实现进度通知
IProgress<T>接口用于在异步操作中报告进度:
static void DoProcessing(IProgress<int> progress)
{
for (int i = 0; i <= 100; ++i)
{
Thread.Sleep(100);
if (progress != null)
{
progress.Report(i);
}
}
}
static async Task Display()
{
var progress = new Progress<int>(percent =>
{
Console.Clear();
Console.Write("{0}%", percent);
});
await Task.Run(() => DoProcessing(progress));
Console.WriteLine("");
Console.WriteLine("结束");
}
九、Factory.FromAsync的应用
Factory.FromAsync用于将APM模式(BeginXXX和EndXXX)转换为任务:
AsynchronousTask d = Test;
Task<string> task = Task<string>.Factory.FromAsync(
d.BeginInvoke("AsyncTaskThread", Callback, "a delegate asynchronous call"), d.EndInvoke);
task.ContinueWith(t => Console.WriteLine("Callback is finished, now running a continuation! Result: {0}",
t.Result));
十、任务的组合与链式操作
1. ContinueWith
使用ContinueWith可以在任务完成后执行后续操作:
Task<int> task = new Task<int>(() => {
int sum = 0;
for (int i = 0; i < 100; i++)
{
sum += i;
}
return sum;
});
task.Start();
Task cwt = task.ContinueWith(t => {
Console.WriteLine("任务完成后的执行结果:{0}", t.Result.ToString());
});
2. 任务的串行与并行
// t1先执行
var t1 = Task.Factory.StartNew(() => {
stack.Push(1);
stack.Push(2);
});
// t2,t3并行执行
var t2 = t1.ContinueWith(t => {
int result;
stack.TryPop(out result);
Console.WriteLine("Task t2 result={0}", result);
});
var t3 = t1.ContinueWith(t => {
int result;
stack.TryPop(out result);
Console.WriteLine("Task t3 result={0}", result);
});
// 等待t2和t3执行完
Task.WaitAll(t2, t3);
3. 子任务
使用TaskCreationOptions.AttachedToParent创建子任务,父任务会等待所有子任务完成:
Task<string[]> parent = new Task<string[]>(state => {
string[] result = new string[2];
// 创建并启动子任务
new Task(() => { result[0] = "我是子任务1。"; }, TaskCreationOptions.AttachedToParent).Start();
new Task(() => { result[1] = "我是子任务2。"; }, TaskCreationOptions.AttachedToParent).Start();
return result;
}, "我是父任务");
parent.ContinueWith(t => {
Array.ForEach(t.Result, r => Console.WriteLine(r));
});
parent.Start();
十一、任务的状态管理
任务有多种状态,如Created、Running、RanToCompletion、Faulted、Canceled等:
Task<int> task2 = CreateTask("Task 3");
Console.WriteLine(task2.Status); // Created
task2.Start();
while (!task2.IsCompleted)
{
Console.WriteLine(task2.Status); // Running
Thread.Sleep(TimeSpan.FromSeconds(0.5));
}
Console.WriteLine(task2.Status); // RanToCompletion
十二、线程池线程与非线程池线程
可以通过Thread.CurrentThread.IsThreadPoolThread属性判断当前线程是否为线程池线程:
Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
十三、总结
C#提供了多种线程使用方式,从基本的Thread类到现代的Task并行库,每种方式都有其特定的适用场景:
- Thread类:适用于需要直接控制线程的场景,但管理复杂。
- Task类:提供了更高级的抽象,是现代C#中推荐的并发编程方式。
- async/await:使异步代码更易于编写和理解,是处理I/O密集型操作的最佳选择。
- Task并行库:提供了丰富的API,如Task.WhenAll、Task.WhenAny等,方便处理复杂的并发场景。
在实际开发中,应根据具体需求选择合适的线程使用方式,以达到最佳的性能和可维护性。
十四、代码示例
本文中的代码示例均来自实际项目,展示了C#中线程的各种使用方式。通过这些示例,我们可以看到C#在并发编程方面的强大能力和灵活性。
无论是简单的异步操作,还是复杂的并行计算,C#都提供了相应的工具和API,帮助开发者更高效地实现并发编程。
十五、最佳实践
- 优先使用Task并行库和async/await模式,而不是直接使用Thread类。
- 合理使用TaskCreationOptions,如LongRunning、AttachedToParent等。
- 正确处理任务中的异常,使用try/catch或AggregateException.Handle。
- 合理使用CancellationTokenSource进行任务取消。
- 使用IProgress
接口实现进度通知,提升用户体验。 - 避免在UI线程中执行长时间运行的操作,使用Task.Run将其转移到后台线程。
- 合理使用Task.Wait和Task.Result,避免不必要的阻塞。
- 考虑使用Task.WhenAll和Task.WhenAny处理多个任务的并行执行。
通过掌握这些线程使用方式和最佳实践,我们可以编写出更高效、更可靠的并发代码,充分利用现代计算机的多核性能。

浙公网安备 33010602011771号