C#中的线程简介

C#中的线程简介

为什么要使用线程

  1. 同一时间智能运行一个任务,长时间运行的任务独占整个计算机,造成其他程序无法响应。
  2. 如果程序有bug会造成整个机器停止工作,用户只好重启计算机,造成数据丢失。

于是,人们为操作系统引入了线程的概念。线程就是对CPU的虚拟化。一个线程就是一个逻辑上的CPU。物理上的CPU会按照调度执行每个线程上的指令。当一个线程出现问题时,也不会影响到其他的线程。
这样,整个操作系统就更加健壮、易于伸缩和安全了。在安装了多个CPU的机器上,计算机是可以真正的同时运行多个线程的,这由操作系统进行保证。

线程的代价

  1. 线程需要占用内存空间
  2. 线程切换也会增加额外的CUP计算时间
  3. 线程的创建和销毁需要消耗大量时间

.Net中使用线程的方法

  1. 定义两个方法模拟耗时的操作
public static void Work1()
{
     Console.WriteLine($"Work1 开始 id:{Thread.CurrentThread.ManagedThreadId}");
     for (int i = 0; i < 25; i++)
     {
         Console.WriteLine($"Work2 {i}");
         Thread.Sleep(10);
     }
     Console.WriteLine($"Work1 结束 id:{Thread.CurrentThread.ManagedThreadId}");
 }
 public static int Work2()
 {
     Console.WriteLine($"Work2 开始 id:{Thread.CurrentThread.ManagedThreadId}");
     int count = 0;
     for (int i = 0; i < 100; i++)
     {
         Console.WriteLine($"Work1 {i}");
         Thread.Sleep(10);
         count += 1;
     }
     Console.WriteLine($"Work2 结束 id:{Thread.CurrentThread.ManagedThreadId}");
     return count;
 }
  1. 使用Thread执行两个方法
public static void UseThread()
{
    Console.WriteLine($"UseThread 开始 id:{Thread.CurrentThread.ManagedThreadId}");

    Thread thread1 = new(Work1);//创建一个线程
    thread1.Start();//启动线程

    Thread thread2 = new(() => Work2());//由于Work2有返回值,用一个Lambda表达式包装后传入
    thread2.Start();

    Console.WriteLine($"UseThread 结束 id:{Thread.CurrentThread.ManagedThreadId}");
}
  1. 为了减少创建和销毁线程的开销,可以使用ThreadPool(线程池),它可以复用线程,从而提高了效率。
public static void UseThreadPool()
{
    Console.WriteLine($"UseThreadPool 开始 id:{Thread.CurrentThread.ManagedThreadId}");

    ThreadPool.QueueUserWorkItem(t => Work1());
    ThreadPool.QueueUserWorkItem(t => Work2());

    Console.WriteLine($"UseThreadPool 结束 id:{Thread.CurrentThread.ManagedThreadId}");
}
  1. 直接使用线程执行任务时,在处理任务结果获取、线程取消、多线程任务协调等场景时,显得非常笨拙。于是在线程池的基础上,.Net推出了Task
public static void UseTask()
{
    Console.WriteLine($"UseTask 开始 id:{Thread.CurrentThread.ManagedThreadId}");

    Task task1 = new(Work1);
    task1.Start();

    Task task2 = new(() => Work2());
    task2.Start();

    //上面跟直接使用Thread类似
    //下面也可以用TaskFactory启动任务

    //Task.Factory.StartNew(Work1);
    //Task.Factory.StartNew(() => Work2());

    Console.WriteLine($"UseTask 结束 id:{Thread.CurrentThread.ManagedThreadId}");
}
  1. 以上看起来跟使用Thread没什么差别,下面我们来看看使用Task的便利之处
//等待任务执行完成
public static void UseTaskWait()
{
    Console.WriteLine($"UseTaskWait 开始 id:{Thread.CurrentThread.ManagedThreadId}");

    var task1 = Task.Factory.StartNew(Work1);
    var task2 = Task.Factory.StartNew(() => Work2());

    //Task.WaitAll(task1,task2);//两个任务都执行完成后,才继续执行后面的代码
    Task.WaitAny(task1, task2);//两个任务任何一个完成,就继续执行后面的代码

    Console.WriteLine($"UseTaskWait 结束 id:{Thread.CurrentThread.ManagedThreadId}");
}

//任务执行完成后执行ContinueWith内的代码,并获得了返回值
public static void UseTaskContinueWith()
{
    Console.WriteLine($"UseTaskContinueWith 开始 id:{Thread.CurrentThread.ManagedThreadId}");

    Task.Run(Work1);

    //此方法会异步执行,ContinueWith中传入回调函数,用于处理Work2返回的结果
    Task.Run(Work2).ContinueWith(r =>
    {
        Console.WriteLine($"Work2 result {r.Result}");
    });

    Console.WriteLine($"UseTaskContinueWith 结束 id:{Thread.CurrentThread.ManagedThreadId}");
}

//获取任务的执行结果
public static void UseTaskResult()
{
    Console.WriteLine($"UseTaskResult 开始 id:{Thread.CurrentThread.ManagedThreadId}");

    var task2 = Task.Run(Work2);

    task2.Wait();//这里会阻塞,等待task2执行完毕
    Console.WriteLine($"task2's result:{task2.Result}");

    //或者直接这样写

    //var result = task2.Result;//这里会阻塞,等待task2执行完毕
    //Console.WriteLine($"task2's result:{result}");

    Console.WriteLine($"UseTaskResult 结束 id:{Thread.CurrentThread.ManagedThreadId}");
}
  1. 为了使用更方便,.Net上引入了async/await关键字,于是我们可以这样使用
public static async void CallMethodAsync1()
{
    Console.WriteLine($"CallMethodAsync1 开始 id:{Thread.CurrentThread.ManagedThreadId}");

    var result = await Task.Run(Work2);
    Console.WriteLine($"result:{result}");

    Console.WriteLine($"CallMethodAsync1 结束 id:{Thread.CurrentThread.ManagedThreadId}");
}

如果任务本身就是异步的

public static Task<int> Work3()
{
    Console.WriteLine($"Work3 开始 id:{Thread.CurrentThread.ManagedThreadId}");

    var result = Task.Run(() =>
    {
        int count = 0;
        for (int i = 0; i < 100; i++)
        {
            Console.WriteLine($"Work1 {i}");
            Thread.Sleep(10);
            count += 1;
        }
        return count;
    });
    Console.WriteLine($"Work3 结束 id:{Thread.CurrentThread.ManagedThreadId}");
    return result;
}
//可以这样调用,有同样的效果

public static async void CallMethodAsync2()
{
    Console.WriteLine($"CallMethodAsync2 开始 id:{Thread.CurrentThread.ManagedThreadId}");

    var result = await Work3();

    Console.WriteLine($"result:{result}");

    Console.WriteLine($"CallMethodAsync2 结束 id:{Thread.CurrentThread.ManagedThreadId}");
}
posted @ 2024-02-28 12:53  MM4650  阅读(16)  评论(0编辑  收藏  举报