[.net 面向对象程序设计进阶] (16) 多线程(Multithreading)(一) 利用多线程提高程序性能(上)
本节导读:
随着硬件和网络的高速发展,为多线程(Multithreading)处理并行任务,提供了有利条件。
其实我们每时每刻都在享受多线程带来的便利,多核处理器多线程工作、Windows操作系统、Web服务器都在使用多线程工作。
使用多线程直接提高了程序的执行效率,因此学习多线程对提高程序运行能力非常必要,本节主要介绍多线程原理及.NET中多线程在.NET面向对象程序设计中的应用。
读前必备:
本节需要了解Lambda表达式基础及匿名方法和匿名委托相关知识;
A. 委托 [.net 面向对象编程基础] (20) 委托
B. Lamda表达式 [.net 面向对象程序设计进阶] (5) Lamda表达式(一) 创建委托
1. 关于多线程
在介绍多线程之前,先了解一下进程。
进程:独立运行的程序称为进程。(比如Windows系统后台程序,也可以称为后台进程)
线程:对于同一个程序分为多个执行流,称为线程。
多线程:使用多个线程进行多任务处理,称为多线程。
2. 如何合理使用多线程?
A.对于用户等待程序处理时,可以使用多线程处理耗时任务;
B.对于一些不需要即时完成的任务,可以使用后台任务线程处理;
C.对于多并发任务,可以使用多线程同时处理;
D.对于通讯类,比如对线程阻塞,可以使用多线程。
除过上面的几个常用的情况,还有很多情况下可以使用多线程。
3. 多线程的缺点
线程自然也有缺点,以下列出了一些:
A.如果有大量的线程,会影响性能,因为操作系统需要在他们之间切换;
B.更多的线程需要更多的内存空间;
C.线程会给程序带来更多的bug,因此要小心使用,比如:线程任务在执行完成后,要及时释放内存;
D.线程的中止需要考虑其对程序运行的影响。
4. .NET中的两种多线程
.NET本身就是一个多线程的的环境。
在.NET中有两种多线程的:
一种是使用Thread类进行线程的创建、启动,终止等操作。
一种是使用ThreadPool类用于管理线程池.
5 .NET中使用Thread进行多线程处理
5.1 Thread类常用方法
.NET基础类库的System.Threading命名空间提供了大量的类和接口支持多线程。System.Threading.Thread类是创建并控制线程,设置其优先级并获取其状态最为常用的类。
下面是该类几个至关重要的方法:
Thread.Start():启动线程的执行;
Thread.Suspend():挂起线程,或者如果线程已挂起,则不起作用;
Thread.Resume():继续已挂起的线程;
Thread.Interrupt():中止处于Wait或者Sleep或者Join线程状态的线程;
Thread.Join():阻塞调用线程,直到某个线程终止时为止
Thread.Sleep():将当前线程阻塞指定的毫秒数;
Thread.Abort():以开始终止此线程的过程。如果线程已经在终止,则不能通过Thread.Start()来启动线程。
下面是一个简单的线程示例:
先创建一个方法:
//简单线程方法
static void MyThreadStart()
{
Console.WriteLine("我是一个简单线程");
}
创建线程调用方法:
//简单的线程
Thread myThread = new Thread(MyThreadStart);
myThread.Start();
运行结果如下:
5.2 Thread类常用属性
Thread的属性有很多,我们先看最常用的几个:
CurrentThread :用于获取当前线程;
ThreadState 当前线程的状态(5.4介绍);
Name:获取或设置线程名称;
Priority:获取或设置线程的优先级(5.5介绍)
ManagedThreadId:获取当前线程的唯一标识
IsBackground:获取或设置线程是前台线程还是后台线程(5.6介绍)
IsThreadPoolThread:获取当前线程是否是托管线程池(后面章节会介绍)
下面创建一个线程示例,来说明这几个属性:
Thread myThreadTest = new Thread(() =>
{
Thread.Sleep();
Thread t = Thread.CurrentThread;
Console.WriteLine("Name: " + t.Name);
Console.WriteLine("ManagedThreadId: " + t.ManagedThreadId);
Console.WriteLine("State: " + t.ThreadState);
Console.WriteLine("Priority: " + t.Priority);
Console.WriteLine("IsBackground: " + t.IsBackground);
Console.WriteLine("IsThreadPoolThread: " + t.IsThreadPoolThread);
})
{
Name = "线程测试",
Priority = ThreadPriority.Highest
};
myThreadTest.Start();
Console.WriteLine("关联进程的运行的线程数量:"+System.Diagnostics.Process.GetCurrentProcess().Threads.Count);
运行结果如下:
5.3 带参数的线程方法
首先我们使用Lambda表达式来改写前面“简单线程”中无参数的方法,如下:
//线程一
new Thread(()=>{
for (int i = ; i < ; i++)
Console.WriteLine("我的线程一-[{0}]", i);
}).Start();
运行结果如下:
上面示例创建的线程并没有带参数,如果是一个有参数的方法,线程该如何创建?
别担心,.NET为我们提供了一个ParameterizedThreadStart委托来解决带一个参数的问题,如下:
//线程二
new Thread((num) =>{
for (int i = ; i < (int)num; i++)
Console.WriteLine("我的线程二--[{0}]", i);
}).Start();
运行结果如下:
那么问题来了,ParameterizedThreadStart委托只有一个包含数据的参数,对于多个参数呢?我们可以使用一个无参数的方法来包装它,如下:
先创建一个带参数的方法:
static void myThreadStart(int numA, int numB)
{
for (int i = (int)numA; i < (int)numB; i++)
Console.WriteLine("我的线程三---[{0}]", i);
}
然后通过无参数的委托来包装它,如下:
//线程三
new Thread(() =>myThreadStart(,)).Start();
运行结果如下:
5.4 Thread状态
我们对于线程启动以后,如何进行挂起和终止、重新启用,首先线程在运行后有一个状态。
System.Threading.Thread.ThreadState属性定义了执行时线程的状态。线程从创建到线程终止,它一定处于其中某一个状态。
A.Unstarted:当线程被创建时,它处在Unstarted状态。
B.Running:Thread类的Start() 方法将使线程状态变为Running状态,线程将一直处于这样的状态,除非我们调用了相应的方法使其挂起、阻塞、销毁或者自然终止。
C.Suspended:如果线程被挂起,它将处于Suspended状态。
D.Running:我们调用resume()方法使其重新执行,这时候线程将重新变为Running状态。一
E.Stopped:旦线程被销毁或者终止,线程处于Stopped状态。处于这个状态的线程将不复存在,正如线程开始启动,线程将不可能回到Unstarted状态。
F.Background:线程还有一个Background状态,它表明线程运行在前台还是后台。在一个确定的时间,线程可能处于多个状态。
G.WaitSleepJoin、AbortRequested:举例子来说,一个线程被调用了Sleep而处于阻塞,而接着另外一个线程调用Abort方法于这个阻塞的线程,这时候线程将同时处于WaitSleepJoin和AbortRequested状态。
H.一旦线程响应转为Sle阻塞或者中止,当销毁时会抛出ThreadAbortException异常。
ThreadState枚举的10种执行状态如下:
对于线程阻塞和同步问题,将在下一节继续介绍。
5.5. 线程优先级
对于多线程任务,我们可以根据其重要性和运行所需要的资源情况,设置他的优先级
System.Threading.ThreadPriority枚举了线程的优先级别,从而决定了线程能够得到多少CPU时间。
高优先级的线程通常会比一般优先级的线程得到更多的CPU时间,如果不止一个高优先级的线程,操作系统将在这些线程之间循环分配CPU时间。
低优先级的线程得到的CPU时间相对较少,当这里没有高优先级的线程,操作系统将挑选下一个低优先级的线程执行。
一旦低优先级的线程在执行时遇到了高优先级的线程,它将让出CPU给高优先级的线程。
新创建的线程优先级为一般优先级,我们可以设置线程的优先级别的值,如下面所示:
对于线程的优先级我们下面做一个实验,开启两个线程(一个设置高优先级,另一个设置低优先级)。
分别委托两个方法进行累加,看一下最终结果,代码如下:
int numberA = , numberB = ;
bool state = true;
new Thread(() => { while (state)numberA++; }) { Priority = ThreadPriority.Highest, Name="线程A" }.Start();
new Thread(() => { while (state)numberB++; }) { Priority = ThreadPriority.Lowest , Name="线程B" }.Start();
//让主线程挂件1秒
Thread.Sleep();
state = false;
Console.WriteLine("线程A: {0}, 线程B: {1}", numberA, numberB);
运行结果如下:
.NET根据优先级分配了资源,可以看到优先级较高的线程执行的机会较大,但优先级小的线程仍然有较多的机会执行。
5.6 前台线程和后台线程
线程有两种,默认情况下为前台线程,要想设置为后台线程也非常容易,只需要加一个属性:thread.IsBackground = true;就可以变为一个后台线程了。
重点来了,前后台线程的区别:
A.前台线程:应用程序必须执行完所有的前台线程才能退出;
B.后台线程:应用程序不必考虑其是否全部完成,可以直接退出。应用程序退出时,自动终止后台线程。
下面我们使用一个输出从0到1000的数字,来实验一下前台线程和后台线程的区别:
Thread myThread = new Thread(() =>{for (int i = ; i < ; i++)Console.WriteLine(i);}); var key = Console.ReadLine();
if (key == "")
{
myThread.IsBackground = true;
myThread.Start();
}
else
{
myThread.IsBackground = false;
myThread.Start();
}
当我们在控制台等级输入的时候,
如果输入1(后台线程),线程会很快关闭,并不会等输出完1000个数字再关闭;
如果输入其它,回车后,则线程会等1000个数字输出完后,窗口关闭;
6. 本节要点:
A.本节主要介绍了线程的基本知识;
B.Thread常用的属性、方法;
C.Thread委托的方法有多个参数的用法;
D.Thread的优先级;
E.Thread的执行状态;
F.前台线程和后台线程;
后面会继续深入介绍利用线程提高程序性能。
==============================================================================================
<如果对你有帮助,记得点一下推荐哦,如有有不明白或错误之处,请多交流>
<对本系列文章阅读有困难的朋友,请先看《.net 面向对象编程基础》>
<转载声明:技术需要共享精神,欢迎转载本博客中的文章,但请注明版权及URL>
.NET 技术交流群:467189533
==============================================================================================
[.net 面向对象程序设计进阶] (16) 多线程(Multithreading)(一) 利用多线程提高程序性能(上)的更多相关文章
-
[.net 面向对象程序设计进阶] (15) 缓存(Cache)(二) 利用缓存提升程序性能
[.net 面向对象程序设计进阶] (15) 缓存(Cache)(二) 利用缓存提升程序性能 本节导读: 上节说了缓存是以空间来换取时间的技术,介绍了客户端缓存和两种常用服务器缓布,本节主要介绍一种. ...
-
[.net 面向对象程序设计进阶] (18) 多线程(Multithreading)(三) 利用多线程提高程序性能(下)
[.net 面向对象程序设计进阶] (18) 多线程(Multithreading)(二) 利用多线程提高程序性能(下) 本节导读: 上节说了线程同步中使用线程锁和线程通知的方式来处理资源共享问题,这 ...
-
[.net 面向对象程序设计进阶] (17) 多线程(Multithreading)(二) 利用多线程提高程序性能(中)
[.net 面向对象程序设计进阶] (17) 多线程(Multithreading)(二) 利用多线程提高程序性能(中) 本节要点: 上节介绍了多线程的基本使用方法和基本应用示例,本节深入介绍.NET ...
-
[.net 面向对象程序设计进阶] (1) 开篇
[.net 面向对象程序设计进阶] (1) 开篇 上一系列文章<.net 面向对象编程基础>写完后,很多小伙伴们希望我有时间再写一点进阶的文章,于是有了这个系列文章.这一系列的文章中, 对 ...
-
[.net 面向对象程序设计进阶] (21) 反射(Reflection)(下)设计模式中利用反射解耦
[.net 面向对象程序设计进阶] (21) 反射(Reflection)(下)设计模式中利用反射解耦 本节导读:上篇文章简单介绍了.NET面向对象中一个重要的技术反射的基本应用,它可以让我们动态的调 ...
-
[.net 面向对象程序设计进阶] (25) 团队开发利器(四)分布式版本控制系统Git——使用GitStack+TortoiseGit 图形界面搭建Git环境
[.net 面向对象程序设计进阶] (25) 团队开发利器(四)分布式版本控制系统Git——使用GitStack+TortoiseGit 图形界面搭建Git环境 本篇导读: 前面介绍了两款代码管理工具 ...
-
[.net 面向对象程序设计进阶] (19) 异步(Asynchronous) 使用异步创建快速响应和可伸缩性的应用程序
[.net 面向对象程序设计进阶] (19) 异步(Asynchronous) 使用异步创建快速响应和可伸缩性的应用程序 本节导读: 本节主要说明使用异步进行程序设计的优缺点及如何通过异步编程. 使用 ...
-
[.net 面向对象程序设计进阶] (4) 正则表达式 (三) 表达式助手
[.net 面向对象程序设计进阶] (2) 正则表达式(三) 表达式助手 上面两节对正则表达式的使用及.NET下使用正则表达式作了详细说明,本节主要搜集整理了常用的正则表达式提供参考. 此外为了使用方 ...
-
[.net 面向对象程序设计进阶] (28) 结束语——告别2015
[.net 面向对象程序设计进阶] (28) 结束语——告别2015 <.net面向对象程序设计进阶>这一系列文章写了太长的时间了,大概有半年没写,在年底又一口气写了好几篇.在整个过程中目 ...
随机推荐
-
IOS中的编码规范
1.指导原则 [原则1-]首先是为人编写程序,其次才是计算机. 说明:这是软件开发的基本要点,软件的生命周期贯穿产品的开发.测试.生产.用户使用.版本升级和后期维护等长期过程,只有易读.易维护的软件代 ...
-
传智播客JavaWeb day07、day08-自定义标签(传统标签和简单标签)、mvc设计模式、用户注册登录注销
第七天的课程主要是讲了自定义标签.简单介绍了mvc设计模式.然后做了案例 1. 自定义标签 1.1 为什么要有自定义标签 前面所说的EL.JSTL等技术都是为了提高jsp的可读性.可维护性.方便性而取 ...
- 9.9,新iPhone要来了,是欢呼,还是墙角画圈,一会儿见分晓
-
斐波那契数列(Fibonacci)递归和非递归实现
序列前9项为:0, 1, 1, 2, 3, 5, 8, 13, 21 要注意非递归的话就是那一个变量帮助存储当前下一项的值,然后依次挪动两个指针往下即可 注意如果n太大 会溢出 ...
-
MongoDB的C#驱动程序教程(译) 转
1.概述 本教程是10gen支持C#驱动程序MongoDB的介绍.假定您熟悉使用MongoDB,因此主要集中在如何使用C#访问MongoDB的. 它分为两个部分:C# 驱动程序 ,BSON图书馆.C# ...
-
Velocity介绍
Velocity是一个基于Java的模版引擎,它是一个简单并且功能强大的开发工具,你可以非常容易地创建和呈现出.在这个介绍当中,我们希望可以给出一个使用基本Velocity的概述. 使用Velocit ...
-
简单的Java逻辑小代码(打擂台,冒泡排序,水仙花数,回文数,递归)
1.打擂台 简单的小代码,打擂台.纪念下过去,祝福下新人. public static void main(String[] args){ int[] ld = {1,4,2,10,8,9,5}; i ...
-
Python 全栈开发二 python基础 字符串 字典 集合
一.字符串 1,在python中,字符串是最为常见的数据类型,一般情况下用引号来创建字符串. >>ch = "wallace" >>ch1 = 'walla ...
-
Java中的构造器与垃圾回收
构造器 在我们初始化对象时,如果希望设置一些默认值,那么就可以使用构造器,在Java中,构造器使用和类同名的名字且没有返回值,如下 class Test{ private String name; T ...
-
python元组()小括号
names = ('jack','rose','tom','jerry','james','jerry') print(names) print(names[0]) #使用元组中的元素 print(n ...