返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >关于C#线程的全面解析
  • 839
分享到

关于C#线程的全面解析

2024-04-02 19:04:59 839人浏览 独家记忆
摘要

目录线程的作用和意义线程生命周期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();
        }
    }
}

运行结果:

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

--结束END--

本文标题: 关于C#线程的全面解析

本文链接: https://www.lsjlt.com/news/146579.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 关于C#线程的全面解析
    目录线程的作用和意义线程生命周期C#创建线程C#让线程休眠一会C#销毁线程C#线程优先级lock:给线程加锁,保证线程同步Monitor:锁定资源Mutex:互斥锁线程的作用和意义 ...
    99+
    2024-04-02
  • 关于Jar包部署命令全面解析
    目录一、nohup xxx &二、-Djava.security.egd=file:/dev/./urandom三、-Duser.timezone=Asia/Shanghai...
    99+
    2022-11-13
    Jar包部署命令 Jar包 Jar命令
  • C++关于树的定义全面梳理
    目录概念树树的叶子节点节点的度分支结点树的度树的高度树的深度二叉树二叉树的特点满二叉树完全二叉树二叉查找树示例代码实现开发环境运行结果概念 本文以一个简单的树为例,如下图,来记录树的...
    99+
    2024-04-02
  • 关于java中线程安全问题详解
    目录一、什么时候数据在多线程并发的环境下会存在安全问题?二、怎么解决线程安全问题?三、银行 取钱/存钱 案例为什么会出现线程安全问题四、总结 一、什么时候数据在多线程并发的环境下会存...
    99+
    2024-04-02
  • Docker中关于Namespace隔离机制全面解析
    目录 前言1. Docker基本架构 服务端 客户端2. Namespace Namespace介绍 Namespace的类型 Mount namespace UTS namespa...
    99+
    2024-04-02
  • C/C++ extern关键字用法示例全面解析
    目录前言一般用法在本模块中使用:跨模块中extern 使用过程中的一些注意事项数组与指针的区别extern 声明全局变量的内部实现extern "C"C和C++互...
    99+
    2023-01-04
    C/C++ extern关键字 C/C++ extern
  • C++中关于互斥量的全面认知
    目录互斥量(保护对共享变量的访问)1.概念2.状态3.特点互斥量的分配1.静态分配2.动态分配加锁和解锁互斥量1.创建互斥锁2.初始化互斥锁3.获取互斥锁4.阻塞调用5.非阻塞调用6...
    99+
    2024-04-02
  • shared_ptr线程安全性全面分析
    正如《STL源码剖析》所讲,“源码之前,了无秘密”。本文基于shared_ptr的源代码,提取了shared_ptr的类图和对象图,然后分析了shared_ptr如何保证文档所宣称的...
    99+
    2022-11-15
    shared_ptr 线程安全性
  • Java中关于线程安全的三种解决方式
    三个窗口卖票的例子解决线程安全问题 问题:买票过程中,出现了重票、错票-->出现了线程的安全问题 问题出现的原因:当某个线程操作车票的过程中,尚未操...
    99+
    2024-04-02
  • Java线程安全与非线程安全解析
    ArrayList和Vector有什么区别?HashMap和HashTable有什么区别?StringBuilder和StringBuffer有什么区别?这些都是Java面试中常见的基础问题。面对这样的问题,回答是:ArrayList是非线...
    99+
    2023-05-31
    java 线程安全 ava
  • Android编程中关于单线程模型的理解与分析
    本文讲述了Android编程中关于单线程模型的理解与分析。分享给大家供大家参考,具体如下: 当一个Android程序启动时,Android系统会同时启动一个对应的主线程(Mai...
    99+
    2022-06-06
    模型 单线程 线程 Android
  • 如何进行关于线程池的分析
    今天就跟大家聊聊有关如何进行关于线程池的分析,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。前言平时接触过多线程开发的童鞋应该都或多或少了解过线程池,之前发布的《阿里巴巴 Java 手...
    99+
    2023-06-16
  • C# 线程安全详解
    目录介绍经典生产消费问题介绍QueueConcurrentQueueBlockingCollectionBlockingCollection 枚举BlockingCollection...
    99+
    2024-04-02
  • Java线程池execute()方法源码全面解析
    先看作者给出的注释来理解线程池到底有什么作用 * Thread pools address two different problems: they usually * provid...
    99+
    2024-04-02
  • 关于Golang标准库flag的全面讲解
    目录命令行参数使用详解选项语法flag是怎么解析参数的?自定义数据类型短选项小结前言: 今天来聊聊Go语言标准库中一个非常简单的库flag,这个库的代码量只有1000行左右,却提供了...
    99+
    2024-04-02
  • 关于C++的.cpp文件运行全过程
    目录.cpp文件运行全过程第一步第二步第三步在一个工程中如何单独运行一个cpp文件第一种是禁用其它的.cpp第二种是注释总结.cpp文件运行全过程 今天来写一写.cpp文件是怎么一步...
    99+
    2023-02-24
    C++ .cpp文件 .cpp文件运行 .cpp文件运行全过程
  • 基于自定义Toast全面解析
    Toast一般用来显示一行文字,用法比较固定:Toast.makeText(Context context,String message,int duration);...
    99+
    2023-05-30
    自定义 toast st
  • java中关于线程同步的理解
    首先了解什么是线程?我们可以在计算机上运行各种计算机软件程序。每一个运行的程序可能包括多个独立运行的线程(Thread)。 线程(Thread)是一份独立运行的程序,有自己专用的运行栈。线程有可能和其他线程共享一些资源,比如,内存,文件,数...
    99+
    2021-11-21
    java入门 java 线程同步 理解
  • 关于Redis单线程的正确理解
    很多同学对Redis的单线程和I/O多路复用技术并不是很了解,所以我用简单易懂的语言让大家了解下Redis单线程和I/O多路复用技术的原理,对学好和运用好Redis打下基础。 一、R...
    99+
    2024-04-02
  • Java基础夯实之线程问题全面解析
    目录1. 线程是什么2. 怎样创建线程2.1 继承Thread类2.2 实现Runnable接口2.3 实现Callable接口2.4 使用线程池创建3. 线程的状态4. 线程常用方...
    99+
    2022-11-13
    Java线程解析 Java线程
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作