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并行库,每种方式都有其特定的适用场景:

  1. Thread类:适用于需要直接控制线程的场景,但管理复杂。
  2. Task类:提供了更高级的抽象,是现代C#中推荐的并发编程方式。
  3. async/await:使异步代码更易于编写和理解,是处理I/O密集型操作的最佳选择。
  4. Task并行库:提供了丰富的API,如Task.WhenAll、Task.WhenAny等,方便处理复杂的并发场景。

在实际开发中,应根据具体需求选择合适的线程使用方式,以达到最佳的性能和可维护性。

十四、代码示例

本文中的代码示例均来自实际项目,展示了C#中线程的各种使用方式。通过这些示例,我们可以看到C#在并发编程方面的强大能力和灵活性。

无论是简单的异步操作,还是复杂的并行计算,C#都提供了相应的工具和API,帮助开发者更高效地实现并发编程。

十五、最佳实践

  1. 优先使用Task并行库和async/await模式,而不是直接使用Thread类。
  2. 合理使用TaskCreationOptions,如LongRunning、AttachedToParent等。
  3. 正确处理任务中的异常,使用try/catch或AggregateException.Handle。
  4. 合理使用CancellationTokenSource进行任务取消。
  5. 使用IProgress接口实现进度通知,提升用户体验。
  6. 避免在UI线程中执行长时间运行的操作,使用Task.Run将其转移到后台线程。
  7. 合理使用Task.Wait和Task.Result,避免不必要的阻塞。
  8. 考虑使用Task.WhenAll和Task.WhenAny处理多个任务的并行执行。

通过掌握这些线程使用方式和最佳实践,我们可以编写出更高效、更可靠的并发代码,充分利用现代计算机的多核性能。

posted @ 2026-03-03 17:56  (*_^)?  阅读(1)  评论(0)    收藏  举报