关于C#线程的全面解析

目录
  • 线程的作用和意义
  • 线程生命周期
  • C#创建线程
  • C#让线程休眠一会
  • C#销毁线程
  • C#线程优先级
  • lock:给线程加锁,保证线程同步
  • Monitor:锁定资源
  • Mutex:互斥锁

线程的作用和意义

线程 被定义为程序的执行路径。每个线程都定义了一个独特的控制流。如果您的应用程序涉及到复杂的和耗时的操作,那么设置不同的线程执行路径往往是有益的,每个线程执行特定的工作。

线程是轻量级进程。一个使用线程的常见实例是现代操作系统中并行编程的实现。使用线程节省了 CPU 周期的浪费,同时提高了应用程序的效率。

到目前为止我们编写的程序是一个单线程作为应用程序的运行实例的单一的过程运行的。但是,这样子应用程序同时只能执行一个任务。为了同时执行多个任务,它可以被划分为更小的线程。

线程生命周期

线程生命周期开始于 System.Threading.Thread 类的对象被创建时,结束于线程被终止或完成执行时。

下面列出了线程生命周期中的各种状态:

  • 未启动状态:当线程实例被创建但 Start 方法未被调用时的状况。
  • 就绪状态:当线程准备好运行并等待 CPU 周期时的状况。
  • 不可运行状态:下面的几种情况下线程是不可运行的:
    • 已经调用 Sleep 方法
    • 已经调用 Wait 方法
    • 通过 I/O 操作阻塞
  • 死亡状态:当线程已完成执行或已中止时的状况

C#创建线程

在C# 语言中使用线程时首先需要创建线程,在使用 Thread 类的构造方法创建其实例时,需要用到 ThreadStart 委托或者 ParameterizedThreadStart 委托创建 Thread 类的实例。ThreadStart 委托只能用于无返回值、无参数的方法,而ParameterizedThreadStart 委托则可以用于带参数的方法。

ThreadStar的方式创建

例子:

using System;
using System.Threading;
namespace MultithreadingApplication
{
    class ThreadCreationProgram
    {
        //线程函数
        public static void CallToChildThread()
        {
            Console.WriteLine("Child thread starts");
        }
        static void Main(string[] args)
        {
            //创建ThreadStart的委托实例
            ThreadStart childref = new ThreadStart(CallToChildThread);
            Console.WriteLine("In Main: Creating the Child thread");
            //创建Thread类的实例
            Thread childThread = new Thread(childref);
            childThread.Start(); //开始一个线程
            Console.ReadKey();
        }
    }
}

运行结果:

ParameterizedThreadStart

例子:

using System;
using System.Threading;
namespace MultithreadingApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            //创建一个线程委托对象
            ParameterizedThreadStart pts = new ParameterizedThreadStart(PrintEven);
            Console.WriteLine("In Main: Creating the Child thread");
           // 创建一个线程对象
            Thread childThread = new Thread(pts);
            childThread.Start(10);
            Console.ReadKey();
        }
        //线程跑的函数
        //打印0~n中的偶数
        private static void PrintEven(Object n)
        {
            Console.WriteLine("Child thread started");
            for(int i=0; i<=(int)n; i+=2) //类型转换
            {
                Console.WriteLine(i);
            }
        }
    }
}

运行结果:

C#让线程休眠一会

using System;
using System.Threading;
namespace MultithreadingApplication
{
    class ThreadCreationProgram
    {
        public static void CallToChildThread()
        {
            Console.WriteLine("Child thread starts");
            int sleepfor = 5000;
            Console.WriteLine("Child Thread Paused for {0} seconds", sleepfor / 1000);
            Thread.Sleep(sleepfor);  //让线程暂停 单位毫秒
            Console.WriteLine("Child thread resumes");
        }
        static void Main(string[] args)
        {
            //创建一个线程的委托
            ThreadStart childref = new ThreadStart(CallToChildThread);
            Console.WriteLine("In Main: Creating the Child thread");
            //创建线程的实例
            Thread childThread = new Thread(childref);
            childThread.Start();
            Console.ReadKey();
        }
    }
}

运行结果:最后一行是5s后才打印出来的

C#销毁线程

/*
 销毁线程
 */
using System;
using System.Threading;
namespace MultithreadingApplication
{
    class ThreadCreationProgram
    {
        //委托函数
        public static void CallToChildThread()
        {
            try//引起异常的语句
            {
                Console.WriteLine("Child thread starts");
                for(int counter = 0; counter <= 10; counter++)
                {
                    Thread.Sleep(500);
                    Console.WriteLine(counter);
                }
                Console.WriteLine("Child Thread Completed");

            }
            catch(ThreadAbortException e)//错误处理代码
            {
                Console.WriteLine("Thread Abort Exception");
            }
            finally //执行的语句
            {
                Console.WriteLine("Could't catch the Thread Exception");
            }
        }
        static void Main(string[] args)
        {
            //创建一个线程的委托实例
            ThreadStart childref = new ThreadStart(CallToChildThread);
            Console.WriteLine("In Main: Creating the Child thread");
            //创建一个线程对象
            Thread childThread = new Thread(childref);
            childThread.Start();
            //主线程休眠
            Thread.Sleep(2000);
            Console.WriteLine("In Main:Aborting the Child thread");
            //在调用此方法的线程上引发ThreadAbortException,以开始终止此线程的过程。
            //调用此方法通常会终止线程
            childThread.Abort();
            Console.ReadKey();
        }
    }
}

运行结果:

C#线程优先级

在C#中线程的优先级使用线程的Priority属性设置即可,默认的优先级是Normal。在设置优先级后,优先级高的线程将优先执行。优先级的值通关ThreadPriority枚举类型来设置,从低到高分别为Lowest 、BelowNormal、Normal、 AboveNormal、 Highest。

例子:

using System;
using System.Threading;
namespace MultithreadingApplication
{
    class Program
    {
        //奇数
        public static void PrintOdd()
        {
            Console.WriteLine("List of odd numbers:");
            for (int i = 1; i <= 100; i += 2)
            {
                Console.Write(i + " ");
            }
            Console.WriteLine();
        }
        //偶数
        public static void PrintEven()
        {
            Console.WriteLine("List of even numbers: ");
            for(int i = 0; i<=100; i+=2)
            {
                Console.Write(i + " ");
            }
            Console.WriteLine();
        }
        static void Main(string[] args)
        {
            //创建线程的委托1
            ThreadStart childref1 = new ThreadStart(PrintEven);
            Console.WriteLine("In Main: Creating the Child1 thread");
            //创建线程1的实例
            Thread childThread1 = new Thread(childref1);
            //设置打印偶数优先级为最低
            childThread1.Priority = ThreadPriority.Lowest;
            //创建线程的委托2
            ThreadStart childref2 = new ThreadStart(PrintOdd);
            Console.WriteLine("In Main: Creating the Child2 thread");
            //创建线程2的实例
            Thread childThread2 = new Thread(childref2);
            //设置打印奇数优先级为最高
            childThread2.Priority = ThreadPriority.Highest;
            childThread1.Start();//偶数  低
            childThread2.Start();//奇数  高
            Console.ReadKey();
        }
    }
}

运行的结果:

第一次运行:

第二次:

第三次:

第四次:

小结:

从上面的运行效果可以看出,由于输岀奇数的线程的优先级高于输出偶数的线程,所以在输出结果中优先输出奇数的次数会更多。

此外,每次输出的结果也不是固定的。通过优先级是不能控制线程中的先后执行顺序的,只能是优先级高的线程优先执行的次数多而已。

线程状态控制的方法包括暂停线程 (Sleep)、中断线程 (Interrupt)、挂起线程 (Suspend)、唤醒线程 (Resume)、终止线程 (Abort)。

lock:给线程加锁,保证线程同步

sleep 方法能控制线程的暂停时间,从而改变多个线程之间的先后顺序,但每次调用线程的结果是随机的。线程同步的方法是将线程资源共享,允许控制每次执行一个线程,并交替执行每个线程。在 C# 语言中实现线程同步可以使用 lock 关键字和 Monitor 类、Mutex 类来解决。对于线程同步操作最简单的一种方式就是使用 lock 关键字,通过 lock 关键字能保证加锁的线程只有在执行完成后才能执行其他线程。

lock的语法如下

lock(object)
 {
    //临界区代码
 }

这里 lock 后面通常是一个 Object 类型的值,也可以使用 this 关键字来表示。

最好是在 lock 中使用私有的非静态或负变量或私有的静态成员变量,即使用 Private 或 Private static 修饰的成员。

例如:

private Object obj = new Object();
lock (obj)
{
    //临界区代码
}

一个更具体的实例

using System;
using System.Threading;
namespace MultithreadingApplication
{
    class Program
    {
        //打印偶数
        public void PrintEven()
        {
            //lock上锁保证执行完该线程才跑其他线程
           lock(this)
            {
                for(int i=0; i<=10; i+=2)
                {
                    //获取当前线程的名字
                    Console.WriteLine(Thread.CurrentThread.Name + "--" + i);
                }
            }
        }
        //打印奇数
        public void PrintOdd()
        {
            lock (this)
            {
                for (int i = 1; i <= 10; i += 2)
                {
                    Console.WriteLine(Thread.CurrentThread.Name + "--" + i);
                }
            }
        }
        static void Main(string[] args)
        {
            //因为下面要用到program类中的非静态函数,所以先创建该类对象
            Program program = new Program();
            //创建线程1的托管
            ThreadStart ts1 = new ThreadStart(program.PrintOdd);
            //创建线程1
            Thread t1 = new Thread(ts1);
            t1.Name = "打印奇数的线程";
            //跑线程1
            t1.Start();
            ThreadStart ts2 = new ThreadStart(program.PrintEven);
            Thread t2 = new Thread(ts2);
            t2.Name = "打印偶数的线程";
            t2.Start();
        }
    }

}

运行结果:

Monitor:锁定资源

和lock用法本质是一样的,使用Monitor类锁定资源的语法如下:

Monitor.Enter(object);
try
{
    //临界区代码
}
finally
{
    Monitor.Exit(object);
}

这里的object与lock中的object一样。

具体例子

sing System;
using System.Threading;
namespace MultithreadingApplication
{
    class Program
    {
        public void PrintEven()
        {
            //在指定对象上获取排它锁
            Monitor.Enter(this);
            try//临界区代码
            {
                for(int i=0; i<=10; i+=2)
                {
                    Console.WriteLine(Thread.CurrentThread.Name + "--" + i);
                }
            }
            finally
            {
                //释放指定对象的排它锁
                Monitor.Exit(this);
            }
        }
        public void PrintOdd()
        {
            Monitor.Enter(this);
            try
            {
                for(int i=1; i<=10; i+=2)
                {
                    Console.WriteLine(Thread.CurrentThread.Name + "--" + i);
                }
            }
            finally
            {
                Monitor.Exit(this);
            }
        }
        static void Main(string[] args)
        {
            //下面创建委托对象时调用的是Program类的非静态方法,
            //所先创建一个program对象
            Program program = new Program();
            //实例化一个委托
            ThreadStart ts1 = new ThreadStart(program.PrintOdd);
            //创建一个线程
            Thread t1 = new Thread(ts1);
            //给线程名字赋值
            t1.Name = "打印奇数的线程";
            //开跑线程
            t1.Start();
            ThreadStart ts2 = new ThreadStart(program.PrintEven);
            Thread t2 = new Thread(ts2);
            t2.Name = "打印偶数的线程";
            t2.Start();
        }
    }
}

运行结果:

Monitor 类的用法虽然比 lock 关键字复杂,但其能添加等待获得锁定的超时值,这样就不会无限期等待获得对象锁。使用 TryEnter() 方法可以给它传送一个超时值,决定等待获得对象锁的最长时间。

使用 TryEnter() 方法设置获得对象锁的时间的语法如下:

Monitor.TryEnter(object, 毫秒数 );

Mutex:互斥锁

Mutex类也是用于线程同步操作的类,当多个线程同时访问一个资源识保证只有一个线程访问资源。在Mutex类中,WaitOne()方法用于等待资源被释放,ReleaseMutex()方法用于释放资源。 WaitOne()方法在等待ReleMutex()方法执行后才会结束。

例子:

using System;
using System.Threading;
namespace MultithreadingApplication
{
    class Program
    {
        //创建一个锁对象
        private static Mutex mutex = new Mutex();
        public static void PakingSpace(object num)
        {
            if(mutex.WaitOne())//等待释放资源,当前资源没调用时为true
            {
                try
                {
                    Console.WriteLine("车牌号{0}的车驶入!", num);
                    Thread.Sleep(1000);//线程休眠一秒
                }
                finally
                {
                    Console.WriteLine("车牌号{0}的车离开!", num);
                    mutex.ReleaseMutex(); //释放锁资源
                }
            }
        }
        static void Main(string[] args)
        {
            //创建一个委托带参数的
            ParameterizedThreadStart ts = new ParameterizedThreadStart(PakingSpace);
            //创建一个线程
            Thread t1 = new Thread(ts);
            t1.Start("A123456");
            Thread t2 = new Thread(ts);
            t2.Start("B00000");
            Console.ReadKey();
        }
    }
}

运行结果:

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • C#使用LOCK实现线程同步

    一.简介 线程安全概念:线程安全是指在当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用.不会出现数据不一致或者数据污染. 线程有可能和其他线程共享一些资源,比如,内存,文件,数据库等.当多个线程同时读写同一份共享资源的时候,可能会引起冲突.这时候,我们需要引入线程“同步”机制,即各位线程之间要有个先来后到,不能一窝蜂挤上去抢作一团.线程同步的真实意思和字面意思恰好相反.线程同步的真实意思,其实是“排队”:几个线程之间要排队,一个一个对共享资源进行操

  • C# 如何获取当前进程或线程的ID

    目录 获取当前进程或线程的ID C# 进程读取方法 获取当前进程或线程的ID 如果获得当前进程的Id用: Process[] processes = Process.GetProcesses();  foreach(Process process in processes)  {  if(process.ProcessName == "进程名"  {  MessageBox.Show(process.Id);  }  } Process processes   =Process.Get

  • C#使用ThreadPriority设置线程优先级

    一.简介 如果在应用程序中有多个线程在运行,但一些线程比另一些线程重要,这种情况下可以在一个进程中为不同的线程指定不同的优先级.线程的优先级可以通过Thread类Priority属性设置,Priority属性是一个ThreadPriority型枚举,列举了5个优先等级:AboveNormal.BelowNormal.Highest.Lowest.Normal.公共语言运行库默认是Normal类型的. 二.代码 class Program { static void Main(string[] a

  • C#使用Monitor类实现线程同步

    一.简介 Lock关键字是Monitor的一种替换用法,lock在IL代码中会被翻译成Monitor. lock (obj) { //代码段 } //就等同于 Monitor.Enter(obj); //代码段 Monitor.Exit(obj); Monitor的常用属性和方法: Enter(Object) 在指定对象上获取排他锁. Exit(Object) 释放指定对象上的排他锁. Pulse 通知等待队列中的线程锁定对象状态的更改. PulseAll 通知所有的等待线程对象状态的更改. T

  • C#多线程的ResetAbort()方法

    一.简介 Abort方法可以通过跑出ThreadAbortException异常中止线程,而使用ResetAbort方法可以取消中止线程的操作,下面通过代码演示使用 ResetAbort方法. 二.代码 class Program { static void Main(string[] args) { Thread thread = new Thread(ThreadMethod); //执行的必须是无返回值的方法 thread.Name = "子線程A"; thread.Start(

  • C#多线程的Join()方法

    一.简介 Join方法主要是用来阻塞调用线程,直到某个线程终止或经过了指定时间为止.官方的解释比较乏味,通俗的说就是创建一个子线程,给它加了这个方法,其它线程就会暂停执行,直到这个线程执行完为止才去执行(包括主线程). 二.代码 class Program { static void Main(string[] args) { Thread threadA = new Thread(ThreadMethod); threadA.Name = "線程A"; Thread threadB

  • jstack和线程dump实例解析

    jstack定义: jstack是Java虚拟机自带的一种堆栈跟踪工具. 基本介绍: jstack用于生成java虚拟机当前时刻的线程快照.线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁.死循环.请求外部资源导致的长时间等待等. 线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源. 命令格式: jstack [ option ] pid 基

  • Java并发之串行线程池实例解析

    前言 做Android的这两年时间,通过研究Android源码,也会Java并发处理多线程有了自己的一些理解. 那么问题来了,如何实现一个串行的线程池呢? 思路 何为串行线程池呢? 也就是说,我们的Runnable对象应该有个排队的机制,它们顺序从队列尾部进入,并且从队列头部选择Runnable进行执行. 既然我们有了思路,那我们就考虑一下所需要的数据结构? 既然是从队列尾部插入Runnable对象,从队列头部执行Runnable对象,我们自然需要一个队列.Java的SDK已经给我们提供了很好的

  • Java等待唤醒机制线程通信原理解析

    这篇文章主要介绍了Java等待唤醒机制线程通信原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 线程间通信 概念:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同.比如:线程A用来生成包子的,线程B用来吃包子的,包子可以理解为同一资源,线程A与线程B处理的动作,一个是生产,一个是消费,那么线程A与线程B之间就存在线程通信问题. 为什么要处理线程间通信: 多个线程并发执行时, 在默认情况下CPU是随机切换线程的,当我们需要多个

  • Java创建线程的方式解析

    目录 继承Thread,这里使用匿名内部类 实现Runnable接口,配合Thread类,同样用匿名内部类 FutureTask配合Thread 继承Thread,这里使用匿名内部类 @Slf4j(topic = "c.Test1") public class Test1 { public static void main(String[] args) { //创建线程对象 Thread t = new Thread(){ @Override public void run() { /

  • 四种Java线程池用法解析

    本文为大家分析四种Java线程池用法,供大家参考,具体内容如下 1.new Thread的弊端 执行一个异步任务你还只是如下new Thread吗? new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub } } ).start(); 那你就out太多了,new Thread的弊端如下: a. 每次new Thread新建对象性能差. b. 线程缺乏统一管理,可能无限

  • Linux线程管理必备:解析互斥量与条件变量的详解

    做过稍微大一点项目的人都知道,力求程序的稳定性和调度的方便,使用了大量的线程,几乎每个模块都有一个专门的线程处理函数.而互斥量与条件变量在线程管理中必不可少,任务间的调度几乎都是由互斥量与条件变量控制.互斥量的实现与进程中的信号量(无名信号量)是类似的,当然,信号量也可以用于线程,区别在于初始化的时候,其本质都是P/V操作.编译时,记得加上-lpthread或-lrt哦. 有关进程间通信(消息队列)见:进程间通信之深入消息队列的详解 一.互斥量 1. 初始化与销毁: 对于静态分配的互斥量, 可以

  • 易语言实现双线程的方法解析

    易语言怎样写双线程? 一个线程循环找图.一个线程循环按键F2. .程序集变量 参数, 整数型 .程序集变量 线程句柄1, 整数型 .程序集变量 线程句柄2, 整数型 启动线程 (&子程序1, 参数,线程句柄1) 启动线程 (&子程序2, ,线程句柄2) .子程序 子程序1 .参数 参数1, 整数型 信息框 (参数1, 0, ) 信息框 ("这是线程1的例子", 0, ) .子程序 子程序2 信息框 ("这是线程2的例子", 0, ) 注意: 凡调用到

  • 关于C#线程的全面解析

    目录 线程的作用和意义 线程生命周期 C#创建线程 C#让线程休眠一会 C#销毁线程 C#线程优先级 lock:给线程加锁,保证线程同步 Monitor:锁定资源 Mutex:互斥锁 线程的作用和意义 线程 被定义为程序的执行路径.每个线程都定义了一个独特的控制流.如果您的应用程序涉及到复杂的和耗时的操作,那么设置不同的线程执行路径往往是有益的,每个线程执行特定的工作. 线程是轻量级进程.一个使用线程的常见实例是现代操作系统中并行编程的实现.使用线程节省了 CPU 周期的浪费,同时提高了应用程序

  • Android后台线程和UI线程通讯实例

    本节向你展示如何在任务中发送数据给UI线程里的对象,这个特性允许你在后台线程工作,完了在UI线程展示结果. 在UI线程定义一个Handler Handler是Android系统线程管理框架里的一部分.一个Handler对象接收消息,并且运行代码来处理消息.正常情况下,你为新线程创建Handler,但你也可以为已有的线程创建一个Handler.当你连接Handler到UI线程时,处理消息的代码会在UI线程上运行. 在创建线程池的类的构造器里实例化Handler对象,保存在全局变量里.用Handle

  • java编写属于自己的线程池

    什么是线程池 线程池就是以一个或多个线程[循环执行]多个应用逻辑的线程集合. 一般而言,线程池有以下几个部分: 完成主要任务的一个或多个线程. 用于调度管理的管理线程. 要求执行的任务队列. 线程池的作用: 线程池作用就是限制系统中执行线程的数量. 根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果:少了浪费了系统资源,多了造成系统拥挤效率不高.用线程池控制线程数量,其他线程排队等候.一个任务执行完毕,再从队列的中取最前面的任务开始执行.若队列中没有等待进程,线程池的这一资源处于

随机推荐