C#异步和多线程以及Thread、ThreadPool、Task区别和使用方法

科技   2024-11-15 07:16   北京  

文章概述

本文旨在帮助读者理解C#中的异步编程与多线程概念,探讨如何在C#中实现多线程,并对比分析不同实现方法的特点及其适用场景。全文分为两大部分,第一部分介绍异步编程模式async/await及其与多线程的区别,第二部分则聚焦于C#中实现多线程的三种主要方法:ThreadThreadPoolTask


第一部分:异步编程模式 async/await 与多线程的区别

1.1 异步编程模式简介

异步编程模式是一种编程接口设计,主要用于处理并发流程需求。async/await是C#中实现异步编程的主要方式之一。这种模式允许开发者编写非阻塞代码,提高程序的响应性和效率。

1.2 async/await 使用示例

static void Main(string[] args)
{
    _ = Async1();
    Console.WriteLine("...............按任意键退出");
    Console.ReadKey();
}

static async Task Async1()
{
    Console.WriteLine("异步开始");
    var r = await Async2();
    var x = await Async3(r);
    Console.WriteLine("结果是 {0}", r + x);
}

static async Task<intAsync2()
{
    await Task.Delay(1000); // 异步延迟方法
    return 100;
}

static async Task<intAsync3(int x)
{
    await Task.Delay(1000);
    return x % 7;
}

执行结果:

通过上述代码可以看到,async关键字修饰的方法为异步方法,而await关键字用于标记异步操作的开始点。只有当两者配合使用时,方法才会真正实现异步执行。

1.3 async/await 的特点

  • 异步执行:在遇到await关键字时,方法会暂停执行,不会阻塞当前线程,而是允许其他任务继续运行。
  • 代码可读性:虽然async/await提高了代码的并发能力,但在某些情况下,可能会降低代码的可读性。
  • 返回值类型async方法可以返回voidTaskTask<T>

第二部分:C# 多线程实现方法

2.1 Thread

Thread类是最基本的多线程实现方式,适用于需要长时间运行的线程。

2.1.1 Thread 使用示例
static void Main(string[] args)
{
    Thread thread = new Thread(Fun1);
    // Thread thread = new Thread(() => Fun1(0)); // 多线程调用时有参数传递的写法
    Console.WriteLine("异步开始");
    // thread.IsBackground = true; // 设置为后台线程
    thread.Start();

    Console.WriteLine("...............按任意键退出");
    Console.ReadKey();
}

static void Fun1()
{
    var r = Fun2();
    var x = Fun3(r);
    Console.WriteLine("结果是 {0}", r + x);
}

static int Fun2()
{
    Thread.Sleep(1000);
    return 100;
}

static int Fun3(int x)
{
    Thread.Sleep(1000);
    return x % 7;
}

执行结果:

2.1.2 Thread 的特点
  • 内存消耗:每次创建新的Thread对象都会消耗一定的内存(约1MB)。
  • 线程类型:默认为前台线程,可以通过IsBackground属性设置为后台线程。
  • 参数传递:可以通过Start方法传递参数,但参数类型必须为object

2.2 ThreadPool 线程池

ThreadPool适用于需要频繁创建和销毁线程且每个线程运行时间较短的场景。

2.2.1 ThreadPool 使用示例
static void Main(string[] args)
{
    Console.WriteLine("主线程执行!");

    ThreadPool.SetMinThreads(11); // 设置最小线程数
    ThreadPool.SetMaxThreads(55); // 设置最大线程数

    for (int i = 1; i <= 10; i++)
    {
        ThreadPool.QueueUserWorkItem(new WaitCallback(testFun), i);
    }

    Console.WriteLine("主线程结束!");

    Console.WriteLine("...............按任意键退出");
    Console.ReadKey();
}

public static void testFun(object obj)
{
    Console.WriteLine(string.Format("{0}:第{1}个线程", DateTime.Now.ToString(), obj.ToString()));
    Thread.Sleep(5000);
}
2.2.2 ThreadPool 的特点
  • 线程池特性:线程池是一个静态类,通过QueueUserWorkItem方法将工作项加入线程池。
  • 性能优势:避免了频繁创建和销毁线程的开销,提高了程序性能。
  • 线程管理:无法直接控制线程的开始、挂起和终止。

2.3 Task

Task类是C#中推荐的多线程实现方式,适用于大多数场景。

2.3.1 Task 使用示例
static void Main(string[] args)
{
    Console.WriteLine("主线程执行!");

    // 方法一
    Task t1 = new Task(() =>
    {
        Console.WriteLine("方法1的任务开始工作……");
        Thread.Sleep(5000);
        Console.WriteLine("方法1的任务工作完成……");
    });
    t1.Start();

    // 方法二
    Task.Run(() =>
    {
        Console.WriteLine("方法2的任务开始工作……");
        Thread.Sleep(5000);
        Console.WriteLine("方法2的任务工作完成……");
    });

    // 方法三
    var t3 = Task.Factory.StartNew(() =>
    {
        Console.WriteLine("方法3的任务开始工作……");
        Thread.Sleep(5000);
        Console.WriteLine("方法3的任务工作完成……");
    });

    Console.WriteLine("主线程结束!");

    Console.WriteLine("...............按任意键退出");
    Console.ReadKey();
}
2.3.2 Task 的特点
  • 灵活性:提供了丰富的API,支持多种管理和控制方法。
  • 性能优化:使用本地队列减少线程之间的资源竞争。
  • 返回值:可以通过Task<T>获取任务的返回值。

2.4 Task 的高级用法

2.4.1 带返回值的 Task
static void Main(string[] args)
{
    Console.WriteLine("主线程执行!");

    Task<int> task = CreateTask("Task 1");
    task.Start();
    int result = task.Result;
    Console.WriteLine("Task 1 Result is: {0}", result);
    Console.WriteLine("主线程结束!");

    Console.WriteLine("...............按任意键退出");
    Console.ReadKey();
}

static Task<intCreateTask(string name)
{
    return new Task<int>(() => TaskMethod(name));
}

static int TaskMethod(string name)
{
    Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
        name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
    Thread.Sleep(TimeSpan.FromSeconds(2));
    return 42;
}
2.4.2 ContinueWith 方法
static void Main(string[] args)
{
    Console.WriteLine("主线程执行!");

    Task t1 = new Task(() =>
    {
        Console.WriteLine("方法1的任务开始工作……");
        Thread.Sleep(5000);
        Console.WriteLine("方法1的任务工作完成……");
    });
    t1.Start();

    t1.ContinueWith(t =>
    {
        Console.WriteLine("方法1的任务工作完成了!");
    });

    Console.WriteLine("主线程结束!");

    Console.WriteLine("...............按任意键退出");
    Console.ReadKey();
}
2.4.3 Task.WaitAll 方法
static void Main(string[] args)
{
    Console.WriteLine("主线程执行!");

    Task t1 = new Task(() =>
    {
        Console.WriteLine("方法1的任务开始工作……");
        Thread.Sleep(5000);
        Console.WriteLine("方法1的任务工作完成……");
    });
    t1.Start();

    Task t2 = new Task(() =>
    {
        Console.WriteLine("方法2的任务开始工作……");
        Thread.Sleep(5000);
        Console.WriteLine("方法2的任务工作完成……");
    });
    t2.Start();

    Task.WaitAll(t1, t2);

    Console.WriteLine("主线程结束!");

    Console.WriteLine("...............按任意键退出");
    Console.ReadKey();
}

总结

多线程是实现异步的一种方法,C#中常用的多线程实现方式包括ThreadThreadPoolTask。具体选择哪种方法取决于应用场景:

  • 长时间运行的线程:推荐使用Thread
  • 频繁创建和销毁线程且运行时间较短:推荐使用ThreadPool
  • 其他一般场景:推荐使用Task,因其灵活性和性能优势。

希望本文能帮助读者更好地理解和应用C#中的异步编程和多线程技术。


参考资料

  • 清华大学出版社《C#从入门到精通(第3版)》
  • 浅析C#中的Thread ThreadPool Task和async/await
  • 谈谈C#的异步和多线程
  • C#多线程与异步的区别
  • c#的async到不是不是异步,它和多线程是什么关系
  • C#线程Thread类
  • C#多线程--线程池(ThreadPool)
  • C#Task详解

请阅读原文

出处:https://www.cnblogs.com/hellohxs/p/14358776.html

版权申明:本文来源于网友收集或网友提供,如果有侵权,请转告版主或者留言,本公众号立即删除。

DotNet开发跳槽
本公众号专注为.net开发工程师提供一个学习技术及求职/跳槽的交流平台。不定期分享NET技术类文章、面试题、求助技巧等干货,原创文章300+篇,让.net开发工程师学习/面试不再迷茫。ps: 后台回复“跳槽”,免费领取.NET开发面试题!
 最新文章