《C#进阶》--06.多线程

本文详细介绍了C#中的多线程概念及使用,包括Thread、ThreadPool、Parallel和Task。强调了多线程的管理成本和性能提升的关系,讲解了线程启动和结束的无序性,并给出了锁(lock)的使用示例。此外,还讨论了await/async在异步编程中的作用,展示了如何通过它们来同步多个Task。
摘要由CSDN通过智能技术生成


提示:以下是本篇文章正文内容,下面案例可供参考

一、基本概念

1)计算机基础

  • 程序:为完成特定任务的一段静态的代码
  • 进程:是程序的一次执行过程,或是正在运行的一个程序,是一个动态的过程,有它自身的产生,存在和消亡的过程。-------生命周期。可以从任务管理器中看到
  • 线程:进程可进一步细化为线程,是一个程序内部的一条执行路径
  • 并行:多个cpu同时执行多个任务。相当于多个人做不同的事
  • 并发:一个cpu(采用时间片)同时执行多个任务。比如:一个人“同时”做多件事,看似是同时,其实采用时间片把每件事轮流执行一小段时间
    请添加图片描述

2)多线程

  • 多线程是属于操作系统的,.net平台只是提供了一个操作我们系统多线程的方式,任何的代码其实也都是这样的事,在操作系统允许的操作内操作,无创造行为。
  • 任何的异步多线程都离不开委托
  • 任何一个简单的程序的运行,都至少开启了一个线程,程序是执行在线程里的
  • 多线程存在管理协调成本,并不是开了多少个多线程,就会有多少的性能提升
  • 线程池创建线程时有上限限制,Thread原生线程无限制,随意开,但是多了之后容易死机
  • 线程的启动和结束都有无序性,CPU在内部随机调配,进行分片处理
  • 使用多线程时,不要试图使用延时等操作去掌控顺序,都是后续难以重现的偶尔错误来源

二、Thread

基本介绍

  • NetFramwork1.0就诞生的古老多线程类
  • Thread的API特别丰富,最接近操作系统的操作,但是因为线程资源是操作系统管理的,而cpu接受指令有一个随机的时间片去执行,并不好控制,响应并不灵敏
  • Thread操作不慎容易造成破坏,操作也更加复杂

基本使用

这是.net最原生的操作多线程的工具类,结构如下:请添加图片描述其中ThreadStart参数就是一个委托:public delegate void ThreadStart();

案例

public void test01()
{
   
    Console.WriteLine("111111111111111111111");
    ThreadStart s = () =>
    {
   
        Console.WriteLine("22222222222222222222222");
    };
    Console.WriteLine("333333333333333333333333333");
    Thread thread = new Thread(s);
    thread.Start();
}

结果:

111111111111111111111
333333333333333333333333333
22222222222222222222222

使用简写的方式:

public void test02()
{
   
    Console.WriteLine("111111111111111111111");
    Console.WriteLine("333333333333333333333333333");
    Thread thread = new Thread(() =>
    {
   
        Console.WriteLine("22222222222222222222222");
    });
    thread.Start();
}

三、ThreadPool

基本介绍

  • NetFramework 2.0诞生的
  • ThreadPool是一种池化资源管理设计思想,用于管理线程资源,包括创建销毁等等。
  • 一般当我们使用线程时,先会申请一个线程,使用完之后就释放掉;而线程池就是一个线程的容器,在使用到时,容器会提前申请几个线程,我们需要使用时,去线程池申请即可。并且对预先申请的线程状态进行控制(用一个字段表示是闲置还是使用中)
  • 可以限制线程的数量,线程的复用
  • API太少,线程等待顺序控制弱

基本使用

 public void test03()
{
   
    Console.WriteLine("111111111111111111111");
    
    WaitCallback callback = o =>
    {
   
        Console.WriteLine("333333333333333333333333333");
        
    };
    ThreadPool.QueueUserWorkItem(callback);
    Console.WriteLine("22222222222222222");
}

四、Parallel

基本介绍

是一个静态类,可以直接调用invoke!!!
invoke可以接受多个Action参数,用来异步执行多个方法
可以通过Parallel0ptions轻松控制最大并发数量

基本结构如下:
请添加图片描述

基本使用

public void test05()
{
   
    Parallel.Invoke(() =>
    {
   
        Console.WriteLine("第1个线程开始执行.........");
        Console.WriteLine("我的线程ID是:"+Thread.CurrentThread.ManagedThreadId);
        Console.WriteLine("第1个线程执行结束.........");
    }, () =>
    {
   
        Console.WriteLine("第2个线程开始执行.........");
        Console.WriteLine("我的线程ID是:" + Thread.CurrentThread.ManagedThreadId);
        Console.WriteLine("第2个线程执行结束.........");
    }, () =>
    {
   
        Console.WriteLine("第3个线程开始执行.........");
        Console.WriteLine("我的线程ID是:" + Thread.CurrentThread.ManagedThreadId);
        Console.WriteLine("第3个线程执行结束.........");
    });
}

结果:

1个线程开始执行.........2个线程开始执行.........
我的线程ID是:62个线程执行结束.........3个线程开始执行.........
我的线程ID是:93个线程执行结束.........
我的线程ID是:11个线程执行结束.........

五、Task

基本介绍

  • NetFramework 3.0诞生
  • Task被称之为多线程的最佳实践
  • Task线程全部是线程池线程
  • 提供了丰富的API,适合开发实践

基本使用

创建线程

/// <summary>
/// Task创建线程
/// </summary>
public static void test01()
{
   
    /**
     * 方式一:Task
     *      new()->Start()
     *      run()
     */
    //1.1 .先创建,再运行
    Task task1 = new Task(() =>
    {
   
        Console.WriteLine("我是task1线程...");
    });
    task1.Start();

    //1.2 创建时将对象返回并运行
    Task task2 = Task.Run(() => 
    {
   
        Console.WriteLine("我是task2线程...");
    });
    Console.WriteLine(task2.Status);

    /**
     * 方式二:
     *      new TaskFactory()->StartNew()
     */
    //2.1 new TaskFactory
    TaskFactory fac1 = new TaskFactory();
    Task task3 = fac1.StartNew(() =>
    {
   
        Console.WriteLine("我是task3线程...");
    });

    //2.2 获取静态对象
    TaskFactory fac2 = Task.Factory;
    Task task4 = fac2.StartNew(() =>
    {
   
        Console.WriteLine("我是task4线程...");
    });
}

几个注意点

1)案例一:窗口应用线程的快速结束
如果我们使用窗体应用程序进行测试,注意下面的写法

public static void Main(string[] args)
{
   
    Task.Run(() =>
    {
   
        Console.WriteLine("11111");
    });
}

这种情况不会打印11111,因为在执行Main方法时,已经存在了一个窗口应用线程,当执行到第三行时,发现是异步操作,会重新创建一个线程,窗口应用线程不会停止,所以窗体应用线程结束了,第二个线程可能才执行到,因为对窗口应用线程来说,没有执行代码,速度会非常快!!!

改进,最好让窗口应用线程sleep2秒,防止窗口线程快速执行结束

public static void Main(string[] args)
{
   
    Task.Run(() =>
    {
   
        Console.WriteLine("11111");
    });
    Thread.Sleep(2000);
}

2)for循环中的多线程

public void test06(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值