多线程详细介绍

摘要:
CurrentPrinciple获取或设置线程的当前负责人。CurrentThread获取当前正在运行的线程。IsAlive获取一个值,该值指示当前线程的执行状态。每个线程都有默认的堆栈大小和优先级,位于多线程池中。通过这种重复使用,应用程序可以避免产生为每个任务创建新线程的开销。

什么是进程线程:我们来看一下自己的任务管理器

多线程详细介绍第1张

这里的每一项都是一个进程,我们的发布的每一个应用程序都需要一个进程去运行,在一个进程内可以有多个线程去计算执行程序。我们看下面的图片:

多线程详细介绍第2张

我们可以看一下进程和线程的数量,很明显可以看出,线程和进程的关系。我们的每一个操作都需要一个线程来执行,鼠标的点击就需要线程去响应我们的操作。

现在我们不难理解,我们一个应用程序就代表一个进程,想让我们的程序高效的运行我们就可以启用多个线程去执行了,当然采用多线程的话有好处也是有代价的,好处合理的利用计资源了,但是线程过多了,你的CPU利用率就加大了,也有可能导致电脑的卡死。

在我们的程序中线程的代表就是:Thread本篇文章我们就说一下线程。我们先了解一下同步异步。

同步:指的是在同一线程下执行,并且会等待结果执行完毕。

异步:不再同一个线程下执行,并且执行得顺序不可控,不会等待执行结果完毕。

我们先用委托来演示一下多线程,如果不怎么了解委托得可以看一下上一篇文章:

    DelegateMethod Method = () =>
            {
           Console.WriteLine($"我的线程ID是:{Thread.CurrentThread.ManagedThreadId}");
            };
            Method.BeginInvoke(null, null);
            Method.Invoke();

本来想用上面的代码去先简单的演示一下,谁知道NetCore 的程序集提供了,但是平台目前还不支持。 我们可以私下使用NET 试一下。

多线程详细介绍第3张

使用Thread 演示

            Console.WriteLine($"*********************************我是同步方法 *******************************");
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine($"我的线程ID是:{Thread.CurrentThread.ManagedThreadId}");
            }
            Console.WriteLine($"*********************************同步方法结束 *******************************");
            Console.WriteLine($"*********************************我是异步方法 *******************************");
            for (int i = 0; i < 10; i++)
            {
                 Thread thread = new Thread(() => { Console.WriteLine($"我的线程ID是:{Thread.CurrentThread.ManagedThreadId}"); });
                 thread.Start();
            }
            Console.WriteLine($"*********************************异步方法结束 *******************************");
            Console.ReadKey();

执行结果:

多线程详细介绍第4张

上面的代码执行结果中我们可以看到:同步方法 是同一个线程来执行的,之上而下有序的执行,但是异步方法启动了多个线程去执行的,并且线程是无序的。看到这样的情况我们就会知道如果我启动了很多线程线程用完之后也是有回收的,回收之后同样会分配的,也就是说同一个操作中线程的ID 可能会多次出现的。

            Console.WriteLine($"*********************************异步启动线程数量计算  *******************************");
            List<int> ListInt = new List<int>();
            int Conut = 0;
            for (int i = 0; i < 2000; i++)
            {
                Thread thread = new Thread(() =>
                {
                    if(ListInt.Contains(Thread.CurrentThread.ManagedThreadId))
                    {
                        Conut++;
                        Console.WriteLine($"我的重复的线程我的 ID是:{Thread.CurrentThread.ManagedThreadId}  重复线程总数量:{Conut}");
                    }
                    ListInt.Add(Thread.CurrentThread.ManagedThreadId);
                });
                thread.Start();
            }
            Console.WriteLine($"*********************************异步启动线程数量计算结果:{ListInt.Count}  *******************************");

结果: 

多线程详细介绍第5张

上面的结果我们可以看到:线程回收再利用。其实thread线程的回收就是我们的GC来做的,这就是C# 的强大之处,自动帮助我们回收了。需要注意的是这样使用线程我们的回收过程是比较慢的,这个回收速度是我们计算机性能决定的。

在上面的结果中我们可以看到我们总共申请了1996个线程,其中有881个线程是重复的,线程的申请和销毁是耗费很多性能的,接下来我们看一下线程池。

线程thread有哪些可操作的属性

CurrentContext

获取线程正在其中执行的当前上下文。

CurrentCulture

获取或设置当前线程的区域性。

CurrentPrinciple

获取或设置线程的当前负责人(对基于角色的安全性而言)。

CurrentThread

获取当前正在运行的线程。

CurrentUICulture

获取或设置资源管理器使用的当前区域性以便在运行时查找区域性特定的资源。

ExecutionContext

获取一个 ExecutionContext 对象,该对象包含有关当前线程的各种上下文的信息。

IsAlive

获取一个值,该值指示当前线程的执行状态。

IsBackground

获取或设置一个值,该值指示某个线程是否为后台线程。

IsThreadPoolThread

获取一个值,该值指示线程是否属于托管线程池。

ManagedThreadId

获取当前托管线程的唯一标识符。

Name

获取或设置线程的名称。

Priority

获取或设置一个值,该值指示线程的调度优先级。

ThreadState

获取一个值,该值包含当前线程的状态。

线程池:ThreadPool

ThreadPool:线程池中的线程都是后台线程IsBackground属性都是True.不会影响所有的前台线程,也就是说不会影响用户体验。每个线程都有默认的堆栈大小和优先级,位于多线程池中。 一旦线程池中的线程完成任务,它将返回到等待线程队列中,这时我们就可以利用这些闲置的线程。通过这种重复使用,应用程序可以避免产生为每个任务创建新线程的开销。在每一个进程中都只会有一个线程池

//public static bool SetMaxThreads(int workerThreads, int completionPortThreads);
            //public static bool SetMinThreads(int workerThreads, int completionPortThreads);
            //workerThreads 工作线程数量  completionPortThreads I/O线程数量
            ThreadPool.SetMaxThreads(12,12);  
            ThreadPool.SetMinThreads(12, 12);

我这里设置工作线程,I/O线程数量来源于我得计算机核心数量,保持每个核心最大最小线程都启动一个。查看计算机处理器核心数量。

多线程详细介绍第6张

上面的设置是说我在线程池中给准备了数量为12 的线程。你可以申请最多12个线程,在使用完之后我会立马进行自动的回收,回收之后的线程继续存放在线程池中等待使用。相比于 Thread线程池ThreadPool对于线程的回收更快,性能更好。

代码看一下性能:

            Stopwatch stopwatch = newStopwatch();
            stopwatch.Start();
            for (int i = 0; i < 1000; i++)
            {
                Thread thread = new Thread(() =>
                {
                    Console.WriteLine($"*********************************我是多线程Thread方法 *******************************");
                });
                thread.Start();
            }
            Console.WriteLine($"*********************************Thread方法结束  耗费时间 :{stopwatch.ElapsedMilliseconds}  *******************************");
            Console.WriteLine($"*********************************多线程ThreadPool启动  *******************************");
            Stopwatch stopwatch1 = newStopwatch();
            stopwatch1.Start();
            for (int i = 0; i < 1000; i++)
            {
                    WaitCallback act = (t) =>
                    {
                        Console.WriteLine($"*********************************我是多线程ThreadPool方法 *******************************");
                    };
                    ThreadPool.QueueUserWorkItem(act);
            }
            Console.WriteLine($"*********************************ThreadPool方法结束  耗费时间 :{stopwatch1.ElapsedMilliseconds}  *******************************");

结果:Thread

多线程详细介绍第7张

结果:ThreadPool

多线程详细介绍第8张

上面的两个方法我们都只是输出一行字符,但是以1000次的来说看一下性能相差有多少。所以建议大家都使用线程池。

推荐官方文档:https://docs.microsoft.com/zh-cn/dotnet/standard/threading/?view=netframework-4.7.2

线程池:Task

随着框架的发展我们有了Task 他也是基于ThreadPool来重新封装的。他的出现方便了我们对多线程的回调等待更好的操作。

推荐一篇博客:https://www.cnblogs.com/lonelyxmas/p/9509298.html

并行计算的多线程Parallel

Parallel 多线程,这个类似同步的,他是在Task的基础之上又一次的封装。假如说我们启动多个线程,她像其他的一样启动了很多的子线程去执行的,而是和当前线程一样并行去执行的。并且当前线程也参与执行。他会卡住线程,等到全部执行完毕后才会继续

这个操作上不如Task 灵活,比如Task 可以等待其中一个线程执行完成后继续主线程,Parallel 是必须等待全部执行完毕。

Parallel里面大致分为三个方法: For,ForEach,Invoke

Invoke:

            Console.WriteLine($"*********************************我是主线程线程 ID:{Thread.CurrentThread.ManagedThreadId} *******************************");
            Action act = () =>{
                Console.WriteLine($"我的线程ID是:{Thread.CurrentThread.ManagedThreadId}");
            };
            Parallel.Invoke(act, act, act, act, act);

结果:

多线程详细介绍第9张

上面结果中我们可以看到主线程参与了进来。

ForEach:

        List<int> vs = new List<int>() { 1, 2, 3, 4, 5};
            Parallel.ForEach<int>(vs, t =>
            {
                Console.WriteLine($"*********************************我是  {t} *******************************");
            });

结果:

多线程详细介绍第10张

For

       List<int> vs = new List<int>() { 1, 2, 3, 4, 5};
            //从零开始 循环多少次
            Parallel.For(0, vs.Count,t=>{
                Console.WriteLine($"*********************************我是  {t} *******************************");
            });

结果:

多线程详细介绍第11张

上面的代码中我们可以看到Parallel 适合在我们循环的时候去使用这样并行的去执行,我们可以减少程序的执行时间。

如果当我们需要执行的集合过大有可能会 并行很多线程时我们怕会影响我们计算机的I/O 我们还可以设置最大的并行数防止程序执行时i/o风暴

//设置 Parallel 最大并行线程的数量
            ParallelOptions options = newParallelOptions();
            //最大并行数为10;
            options.MaxDegreeOfParallelism = 10;

Parallel 官方介绍:https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.tasks.parallel?redirectedfrom=MSDN&view=netframework-4.7.2

获取当前计算机 最大线程数

        intworkerThreads;
            intcompletionPortThreads;
            ThreadPool.GetMaxThreads( out workerThreads, outcompletionPortThreads);
            Console.WriteLine($"最大工作线程:{workerThreads} 最大I/O线程:{completionPortThreads} ");
            ThreadPool.GetMinThreads(out workerThreads, outcompletionPortThreads);
            Console.WriteLine($"最小工作线程:{workerThreads} 最小 I/O线程:{completionPortThreads} ");

结果:

多线程详细介绍第12张

有不足之处希望大家指出相互学习,

转载请注明出处 谢谢!

免责声明:文章转载自《多线程详细介绍》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇angularJS的插件使用iOS WebView 加载本地资源(图片,文件等)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Go语言的调度模型(GPM)

GPM模型 定义于src/runtime/runtime2.go G: Gourtines(携带任务), 每个Goroutine对应一个G结构体,G保存Goroutine的运行堆栈,即并发任务状态。G并非执行体,每个G需要绑定到P才能被调度执行。 P: Processors(分配任务), 对G来说,P相当于CPU核,G只有绑定到P(在P的local ru...

《Java2 实用教程(第五版)》学习指导

《Java2 实用教程(第五版)》 第1章Java入门 主要内容:P1 1.1Java的地位:P1 1.2Java的特点:P2 1.3安装JDK:P5 1.4Java程序的开发步骤:P8 1.5简单的Java应用程序:P9 1.6Java反编译:P13 第2章基本数据类型与数组 主要内容:P17 2.1标识符与关键字:...

数据库并发处理

为什么要有锁? 我们都是知道,数据库中锁的设计是解决多用户同时访问共享资源时的并发问题。在访问共享资源时,锁定义了用户访问的规则。根据加锁的范围,MySQL 中的锁可大致分成全局锁,表级锁和行锁三类。在本篇文章中,会依次介绍三种类型的锁。在阅读本篇文章后,应该掌握如下的内容: 为什么要在备份时使用全局锁? 为什么推荐使用 InnoDB 作为引擎进行备份?...

httpd三种MPM的原理剖析

apache httpd系列文章:http://www.cnblogs.com/f-ck-need-u/p/7576137.html 本文专讲httpd MPM。为了更完整、权威,我先把apache httpd 2.4关于prefork、worker和event的官方手册大致翻译了一遍,也就是本文的前3节。水平有限,难免翻译的"鬼才看得懂",还请见谅。...

分布式系统互斥性与幂等性问题的分析与解决

随着互联网信息技术的飞速发展,数据量不断增大,业务逻辑也日趋复杂,对系统的高并发访问、海量数据处理的场景也越来越多。如何用较低成本实现系统的高可用、易伸缩、可扩展等目标就显得越发重要。 为了解决这一系列问题,系统架构也在不断演进。传统的集中式系统已经逐渐无法满足要求,分布式系统被使用在更多的场景中。 分布式系统由独立的服务器通过网络松散耦合组成。在这个系统...

iOS UI-线程(NSThread)及其安全隐患与通信

一、基本使用 1.多线程的优缺点 多线程的优点 能适当提高程序的执行效率 能适当提高资源利用率(CPU、内存利用率) 多线程的缺点 开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能 线程越多,CPU在调度线程上的开销就越大 程序设计更加复杂:比如线程之间的通信、多线...