zoukankan      html  css  js  c++  java
  • C# 多线程

    线程是进程中某个单一顺序的控制流,是程序运行中的调度单位,是程序执行流的最小单位,一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。 线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。 线程也有就绪、阻塞和运行三种基本状态。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序进程本身。

    CLR中有三种常用创建和管理线程的方式:Thread、ThreadPool、Task
     
    Thread
     

    什么是进程?

    当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源。 而一个进程又是由多个线程所组成的。

    什么是线程?

    线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数。

    什么是多线程?

    多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。

    多线程的好处:

    可以提高CPU的利用率。在多线程程序中,一个线程必须等待的时候,CPU可以运行其它的线程而不是等待,这样就大大提高了程序的效率。

    多线程的不利方面:

    线程也是程序,所以线程需要占用内存,线程越多占用内存也越多; 多线程需要协调和管理,所以需要CPU时间跟踪线程; 线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题; 线程太多会导致控制太复杂,最终可能造成很多Bug;

    接下来将对C#编程中的多线程机制进行探讨。为了省去创建GUI那些繁琐的步骤,更清晰地逼近线程的本质,接下来的所有程序都是控制台程序,程序最后的Console.ReadLine()是为了使程序中途停下来,以便看清楚执行过程中的输出。

    任何程序在执行时,至少有一个主线程。

    一个直观印象的线程示例:

    using System;
    using System.Threading;
    
    namespace ConsoleApplication6
    {
        class Program
        {
            [STAThread]  //指示应用程序的 COM 线程模型是单线程单元 (STA)。 
            static void Main(string[] args)
            {
                Thread.CurrentThread.Name = "System Thread";//给当前线程起名为"System Thread"
                Console.WriteLine(Thread.CurrentThread.Name + "'s  status:" + Thread.CurrentThread.ThreadState);
                Console.ReadLine();
            }
        }
    }

    输出如下:

    System Thread's Status:Running

    在这里,我们通过Thread类的静态属性CurrentThread获取了当前执行的线程,对其Name属性赋值“System Thread”,最后还输出了它的当前状态(ThreadState)。

    所谓静态属性,就是这个类所有对象所公有的属性,不管你创建了多少个这个类的实例,但是类的静态属性在内存中只有一个。很容易理解CurrentThread为什么是静态的——虽然有多个线程同时存在,但是在某一个时刻,CPU只能执行其中一个。

     

     

    我们通过其中提供的Thread类来创建和控制线程,ThreadPool类用于管理线程池等。 (此外还提供解决了线程执行安排,死锁,线程间通讯等实际问题的机制。)

    Thread类有几个至关重要的方法,描述如下:

    Start():启动线程;

    Sleep(int):静态方法,暂停当前线程指定的毫秒数;

    Abort():通常使用该方法来终止一个线程;

    Suspend():该方法并不终止未完成的线程,它仅仅挂起线程,以后还可恢复;

    Resume():恢复被Suspend()方法挂起的线程的执行。

     

    ThreadPool

     

    相关概念:

        线程池可以看做容纳线程的容器;

        一个应用程序最多只能有一个线程池;

        ThreadPool静态类通过QueueUserWorkItem()方法将工作函数排入线程池;

        每排入一个工作函数,就相当于请求创建一个线程; 

    线程池的作用:

       线程池是为突然大量爆发的线程设计的,通过有限的几个固定线程为大量的操作服务,减少了创建和销毁线程所需的时间,从而提高效率。

       如果一个线程的时间非常长,就没必要用线程池了(不是不能作长时间操作,而是不宜。),况且我们还不能控制线程池中线程的开始、挂起、和中止。

    什么时候使用ThreadPool

      线程池线程分为两类工作线程和IO线程,可以单独设置最小线程数和最大线程数: 

      ThreadPool.SetMinThreads(2, 2);

      ThreadPool.SetMaxThreads(4, 4); 

      最大线程数很好理解,就是线程池最多创建这些线程,如果最大4个线程,现在这4个线程都在运行的话,后续进来的线程只能排队等待了。那么为什么有最小线程一说法呢?其实之所以使用线程池是不希望线程在创建后运行结束后理解回收,这样的话以后要用的时候还需要创建,我们可以让线程池至少保留几个线程,即使没有线程在工作也保留。上述语句我们设置线程池一开始就保持2个工作线程和2个IO线程,最大不超过4个线程。

     

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApplication6
    {
        class Program
        {
               static void Main(string[] args)
                {
                     //CancellationTokenSource类   通知 CancellationToken,告知其应被取消。
                     //CancellationToken类         传播有关应取消操作的通知。 
                     //用来取消小黄鱼线程
                     CancellationTokenSource cts = new CancellationTokenSource();    
    
                     //线程池
                      Fish fish1 = new Fish();
                      Fish fish2 = new Fish() { Name = "大鲨鱼" ,Score=100};
                      Fish fish3 = new Fish() { Name = "灯笼鱼" ,Score=50};
                      Fish fish4 = new Fish() { Name = "红鲤鱼" ,Score=20};
                      Fish fish5 = new Fish() { Name = "灯笼鱼" ,Score=10};
    
                      //QueueUserWorkItem()  将方法排入队列以便执行。此方法在有线程池线程变得可用时执行。 参数是要执行的方法
                      ThreadPool.QueueUserWorkItem(f => { fish1.Move(cts.Token); });
                      ThreadPool.QueueUserWorkItem(f => { fish2.Move(cts.Token); });
                      ThreadPool.QueueUserWorkItem(f => { fish3.Move(cts.Token); });
                      ThreadPool.QueueUserWorkItem(f => { fish4.Move(cts.Token); });
                      ThreadPool.QueueUserWorkItem(f => { fish5.Move(cts.Token); });
    
    
                      //参数表示传入一个方法,表示要在任务中执行的代码的委托。
                      Task t1 = new Task(() => fish1.Move(cts.Token), cts.Token);
                      
                      //ContinueWith()线程结束后的回调方法
    
                      //小黄鱼被击中后显示积分
                      t1.ContinueWith(fish1.ShowScore);
     
                      Task t2 = new Task(() =>fish2.Move(cts.Token), cts.Token);             
                      //大鲨鱼鱼被击中后显示积分
                      t2.ContinueWith(fish2.ShowScore);
     
                      //按任意键发射
                      Console.ReadKey();
     
                      //武器工厂线程池,执行一组任务
                      Gun gun = new Gun();
                      LaserGun laserGun = new LaserGun();
                      TaskFactory taskfactory = new TaskFactory();
    
                      Task[] tasks = new Task[]
                      {
                          //TaskFactory.StartNew 方法   创建并启动 Task。 
                            taskfactory.StartNew(()=>gun.Fire()),
                            taskfactory.StartNew(()=>laserGun.Fire())
                      };
    
                      //TaskFactory.ContinueWhenAll 方法  创建一个延续任务,该任务在一组指定的任务完成后开始。 
                      //执行武器们开火
                      taskfactory.ContinueWhenAll(tasks, (Task) => { });
     
                      //鱼儿们被击中了就会去调显示积分的方法
                      cts.Cancel();
                      Console.ReadLine();
                }
          }
     
          class Fish
          {
                public string Name { get; set; }
                public int Score { get; set; }
     
                public Fish()
                {
                      Name = "小黄鱼" ;
                      Score = 1;
                }
     
                /// <summary>
                /// 游动
                /// </summary>
                public void Move(CancellationToken ct)
                {
                      //如果没有被击中,就一直游阿游,用IsCancellationRequested判断
                      while (!ct.IsCancellationRequested)
                      {
                             Console.WriteLine(string .Format("{0}在游来游去......", Name));
                             Thread.Sleep(5000);
                      }                 
                }
     
                //中枪死亡后显示奖励
                public void ShowScore(Task task)
                {
                       Console.WriteLine(string .Format("{0}中弹了,您得到{1}分......" , Name, Score));
                }
          }
     
          abstract class Weapon
          {
                 public string Name { get; set; }
                 public abstract void Fire();
          }
     
          class Gun : Weapon
          {
                public Gun()
                      : base()
                {
                      Name = "双射枪" ;
                }
                public override void Fire()
                {
                      Console.WriteLine(string .Format("咻咻咻,{0}向鱼儿们发射子弹......" , Name));
                }
          }
     
          class LaserGun : Weapon
          {
                public LaserGun()
                      : base()
                {
                      Name = "激光炮" ;
                }
                public override void Fire()
                {
                      Console.WriteLine(string .Format("嗖嗖嗖,{0}向鱼儿们发射炮弹......" , Name));
                }
          }
    }

     用到线程的时候,如果数量少就用Thread,但Thread不太好结束,这时可以考虑用CancellationTokenSource 类的Cancel方法,当要用到很多线程的时候就用线程池ThreadPool,当要用到很多线程且要对对应的线程进行控制和回调的时候就用Task。

     

     

     

  • 相关阅读:
    Python脚本文件(.py)打包为可执行文件(.exe)即避免命令行中包含Python解释器
    MVC 三步完成增删改查设计
    MVC中使用SqlServerCe
    回车转Tab
    动态代码
    Mvc 用户没有登录跳转到登录界面
    Mvc
    Mvc提交
    EF查询 linq
    EF数据迁移 Migrations
  • 原文地址:https://www.cnblogs.com/tech-bird/p/3679041.html
Copyright © 2011-2022 走看看