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

    线程基础

    一个进程由若干个线程组成,线程是程序执行的基本原子单位。线程是"进程"中某个单一顺序的控制流,线程是进程中的一个基本执行流,每个线程都有自己专属的寄存器(程序计数器、栈指针等),代码共享区,不同的线程可以执行同样的方法。

        多线程可以实现并行处理,可以避免某项任务长时间占用CPU时间,需要注意的是,多线程程序对于效率,应该根据任务不同的要求来选择。

    线程的命名空间System.Threading

        Thread类是线程中最重要的一个,Thread类提供了创建并控制线程,设置其优先级并获取其状态的方法。

        Thread类的声明:

    class ThreadSimple

    {

    //静态线程函数

    public static void ThreadMethodExample()

    {

    }

    }

    //调用静态方法

    Thread threadSimple = new Thread(ThreadSimple.ThreadMethodExample);

    或:

    class ThreadSimple

    {

    //静态线程函数

    public static void ThreadMethodExample()

    {

    }

    //调用静态方法

    //Thread threadSimple = new Thread(ThreadSimple.ThreadMethodExample);

    Thread threadSimple = new Thread(new ThreadStart(ThreadMethodExample));

    }

    1. 线程启动、结束

      Thread类的常用方法:

      1. IsAlive:判断线程是否处于活动状态
      2. Name:线程的名称
      3. Priority:ThreadPriority枚举类型,代表线程的优先级{Normal,AboveNormal,BelowNormal,Highest,Lowest}
      4. ThreadState:ThreadState枚举类型,代表线程的状态 { Running(线程已启动,正在执行) , StopRequested(正在请求停止此线程), SuspendRequested(正在请求挂起此线程), Background(线程正在被作为后台线程执行), Unstarted(尚未启动线程),Stopped(线程已经停止),WaitSleepJoin(线程已经被阻止),Suspended(线程已经挂起),AbortRequested(对线程调用了Thread.Abort()方法但是线程尚未收到试图终止它的挂起的System.Threading.ThreadAbortException) , Aborted(线程状态包括AbortRequested,并且该线程现在已死,但其状态尚未更改为Stopped) }
      5. Start:启动一个线程
      6. Suspend:挂起一个线程的运行(暂停、中断)
      7. Resume:继续被挂起的线程,恢复被Suspend()方法挂起的线程的执行
      8. Abort():结束一个线程的运行,终止线程
      9. Sleep():线程的休眠,使线程进入一定时间的休眠状态,时间一到,线程继续执行
    2. 线程间数据同步
      1. 线程间数据共享

        多线程编程中,如果线程间需要共享数据,需要把共享的数据设置为静态类型的,此时可以使用static关键字

      2. Lock语句同步数据访问

        线程之间的同步和通信处理,两个线程需要同时操作一个队列,一个线程进行添加操作,另一个线程进行取用元素操作。

        lock关键字将语句块标记为临界区,方法是获取给定对象的互斥锁,执行语句,然后释放该锁。Lock的语法:lock(expression)statement_block【其中expression是加锁对象,必须是引用类型,不能是数值类型,statement_block代表正在访问的共享资源的程序段】,

        lock语句可以很好地实现互斥操作,从而保护数据在某个时刻内只有一个线程可以操作该数据,直至操作完成才允许其他线程进行操作,这样就实现了按顺序操作的设计,从而避免不可预料的情况发生。

    public static void MethodSubB()

    {

    do

    {

    lock(lockExample)

    {

    i-=1;

    Console.WriteLine("线程2开始,共享数据为:i={0}",i);

    Thread.Sleep(2000); //线程A休眠2秒

    Console.WriteLine("线程2结束,共享数据值i={0}",i);

    }

    } while (true);

    }

        3.Mutex类同步数据访问
        只向某个线程授予对共享资源的独占访问权,如果一个获取了互斥体,那么想要获取该互斥体的其他线程将被挂起,直到这个线程释放该互                 斥体。

        线程可以使用Mutex.WaitOne()方法释放这个对象,而在此期间,其他想要获取这个Mutex对象的线程都只能等待。

        4.Monitor类同步数据访问数

    Monitor类用于锁定对象,一个线程只有得到这把锁才能对该对象进行操作,对象锁保证了在可能引起混乱的情况下,一个时刻只有一个线程可以访问这个对象。

    Monitor必须和一个具体的对象相关联,但由于它是一个静态类,所以不能使用它来定义对象,而且它的所有方法都是静态的,不能使用对象来引用。

    当一个线程调用Monitor.Enter()方法锁定对象时,这个对象就归它所有了,其他线程想要访问这个对象,只有等待它调用Monitor.Exit()方法释放锁。

                Monitor类主要成员

    Enter

    在指定对象上获取排他锁

    Exit

    释放指定对象上的排它锁

    Pluse

    通知等待队列中的线程锁定对象状态的更改

    PluseAll

    通知所有的等待线程对象状态的更改

    TryEnter

    试图获取指定对象上的排他锁

    Wait

    释放对象上的锁并阻止当前线程,直到它重新获取该锁

    private static Object sObjectA = new Object();

    private static Object sObjectB = new Object();

    public static void DemoA()

    {

    if(Monitor.TryEnter(sObjectA,1000))

    {

    Thread.Sleep(1000);

    if (Monitor.TryEnter(sObjectB, 2000))

    {

    Monitor.Exit(sObjectB);

    }

    else

    {

    Console.WriteLine("TryEnter SObjectB超时...");

    }

    Monitor.Exit(sObjectA);

    }

    Console.WriteLine("执行DemoA");

    }

    public static void DemoB()

    {

    if (Monitor.TryEnter(sObjectB, 1000))

    {

    Thread.Sleep(1000);

    if (Monitor.TryEnter(sObjectA, 2000))

    {

    Monitor.Exit(sObjectA);

    }

    else

    {

    Console.WriteLine("TryEnter SObjectA超时...");

    }

    Monitor.Exit(sObjectB);

    }

    Console.WriteLine("执行DemoB");

    }

    static void Main(string[] args)

    {

    Thread threadA = new Thread(DemoA);

    Thread threadB = new Thread(DemoB);

    threadA.Start();

    threadB.Start();

    Thread.Sleep(4000);

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

    }

     

        5.带参数线程

        在不传递参数的情况下,可以使用ThreadStart代理来执行函数,如果要传递参数给执行函数,则可使用ParameterizedThreadStart代理来链接函数

        Thread类的4个重载的构造函数

          1.Thread(ThreadStart)[初始化Thread类的新实例]

          2.Thread(ParameterizedThreadStart)

          [初始化Thread类的新实例,指定允许对象在线程启动时传递给线程的委托]

          3.Thread(ParameterizedThreadStart,Int32)

          4.Thread(ThreadStart,Int32)

          [初始化Thread类的新实例,并指定线程的最大堆栈]

    实例:

    class ThreadDemo

    {

    public int paraA, paraB;

    public void MethodDemo()

    {

    Console.WriteLine("paraA={0},paraB={1}", paraA, paraB);

    }

    public void Print(object obj)

    {

    Console.WriteLine("传入的参数是{0}", obj.ToString());

    }

    }

    static void Main(string[] args)

    {

    ThreadDemo A = new ThreadDemo();

    A.paraA = 2;

    A.paraB = 3;

    Thread threadA = new Thread(new ThreadStart(A.MethodDemo));

    threadA.Start();

    Thread threadB = new Thread(new ParameterizedThreadStart(new ThreadDemo().Print));

    threadB.Start(" 这是传入的参数");

    Console.Write("Press any key to continue...");

    Console.ReadKey();

    }

      6.线程池ThreadPool

    线程池是可以在后台执行多个任务的线程集合,这使得主线程可以自由地异步执行其他任务。一旦池中的某个线程任务完成,它将返回到等待线程队列中等待在此被使用。线程池线程都是后台线程。

    以下情况应该使用单独的线程不宜使用线程池

    1.线程需要指定优先级

    2.线程执行的时间较长

    3.线程在单独的线程apartment中

    4.在线程执行的过程中对线程存在操作

    static void MethodA(object num)

    {

    int QueueNum = (int)num;

    Console.WriteLine("线程号:{0}", QueueNum);

    //输出空行,为了美观

    Console.WriteLine();

    }

    static void Main(string[] args)

    {

    for (int i = 0; i < 5; i++)

    {

    //在线程池中创建线程池线程来执行指定的方法(用WaitCallBack来表示),并将此线程排入线程池的队列等待执行

    ThreadPool.QueueUserWorkItem(new WaitCallback(MethodA), i);

    }

    }

  • 相关阅读:
    读 Kafka 源码写优雅业务代码:配置类
    如何安装FTP服务器,并实现文件共享
    Merge into用法总结
    Insomnia 跟 Postman 类似的软件
    iOS dealloc中初始化weak指针崩溃防护
    Centos7安装febootstrap
    获取 linux 系统 CPU、内存、磁盘 IO 等信息的脚本
    Git本地远程仓库
    网络及服务故障的排查思路
    Git配置远程仓库(密匙链接)
  • 原文地址:https://www.cnblogs.com/weihanli/p/3520863.html
Copyright © 2011-2022 走看看