多线程Thread
多线程的意义
使用多线程可以充分利用CPU资源.提高CPU的使用率,采用多线程的方式去同时完成几件事情而不互相干扰.在处理大量的IO操作或处理的情况需要花费大量的时间时(如:读写文件,视频图像的采集,处理,显示,保存等)有较大优势
优点
- 多线程可以把占据时间长的程序中的任务放到后台去处理而不影响主程序的运行
- 程序的运行效率可能会提高
- 在一些等待的任务实现上如用户输入,文件读取和网络收发数据等,线程比较有用
不足
- 如果有大量的线程,会影响性能,因为操作系统需要在它们之间切换.
- 更多的线程需要更多的内存空间
这里需要了解一下这几个概念
并发(Concurrency)
逻辑上的同时发生,一个处理器(在不同时刻或者说在同一时间间隔内)"同时"处理多个任务。宏观上是并发的,微观上是按排队等待、唤醒、执行的步骤序列执行。并发性是对有限物理资源强制行使多用户共享(多路复用)以提高效率
并行(Parallel)
物理上的同时发生,多核处理器或多个处理器(在同一时刻)同时处理多个任务。并行性允许多个程序同一时刻可在不同CPU上同时执行
进程(Process)
程序在计算机上的一次执行活动。运行一个程序、启动一个进程。程序是死的(静态的),进程是活的(动态的)。Windows系统利用进程把工作划分为多个独立的区域,每个应用程序实例对应一个进程。进程是操作系统分配和使用系统资源的基本单位。进程包含一个运行-ing应用程序的所有资源、进程(占用的资源)间相互独立
线程(Thread)
轻量级进程,是进程的一个实体(线程本质上是进程中一段并发运行的代码),执行线程、体现程序的真实执行情况,是处理器上系统独立调度和时间分配的最基本的执行单元。同一进程的所有线程共享相同的资源和内存(共享代码,全局变量,环境字符串等),使得线程间上下文切换更快、可以在同一地址空间内访问内存
同步
如果一个程序调用某个方法,等待其执行所有处理后才继续执行,称这样的方法是同步的
异步
如果一个程序调用某个方法,在该方法处理完成之前就返回到调用方法,则这个方法是异步的
线程创建
C#中使用Thread类创建和控制线程,该类允许创建线程,以及设置线程的优先级
class ThreadTest
{
static void Main(string[] args)
{
Console.WriteLine("程序在启动时创建一个线程,称为主线程");
//创建线程
Thread t = new Thread(ThreadMethod);
//启动线程
t.Start();
Console.ReadKey();
}
public static void ThreadMethod()
{
Console.WriteLine("ThreadMethod");
}
}
Thread构造函数接收ParameterrizeThreadStart和ThreadStart委托参数,所以也可以这么写
Thread t = new Thread(new ThreadStart(ThreadMethod));
lambad表达式创建
Thread t = new Thread(() => Console.WriteLine("ThreadMethod"));
线程调用顺序
备注:不同PC运行结果可能不一致,只作示例
Thread t = new Thread(() => Console.WriteLine("A"));
Thread t1 = new Thread(() => Console.WriteLine("B"));
Thread t2 = new Thread(() => Console.WriteLine("C"));
Thread t3 = new Thread(() => Console.WriteLine("D"));
t.Start();
t1.Start();
t2.Start();
t3.Start();
第一次:
第二次:
第三次:
说明:线程是由操作系统调度的,每次哪个线程先被执行可以不同,就是说该例子中线程的调度是无序的
线程传递数据
- 使用带ParameterrizeThreadStart委托参数的Thread构造函数
- 创建自定义类.把线程方法定义为实例方法,之后初始化实例数据,启动线程
不带参数
Thread t = new Thread(() => Console.WriteLine("ThreadMethod"));
一个参数
static void Main(string[] args)
{
string name = "w";
Thread t = new Thread(ThreadMethod);
t.Start(name);
Console.ReadKey();
} /// <summary>
/// ParameterizedThreadStart要求参数类型必须为object,所以定义的方法B形参类型必须为object
/// </summary>
/// <param name="obj"></param>
public static void ThreadMethod(object obj)
{
Console.WriteLine("ThreadMethod,参数值{0}", obj);
}
多个参数(自定义类)
class Data
{
public string name;
public int age;
public Data(string name, int age)
{
this.name = name;
this.age = age;
}
public void Write()
{
Console.WriteLine("name:{0},age:{1}", this.name, this.age);
}
}
var data = new Data("Wang", );
Thread t = new Thread(data.Write);
t.Start();
后台线程
默认情况下:用Thread类创建的线程总是前台线程,线程池中的线程总是后台线程
前台线程和后台线程的区别在于
- 前台线程:应用程序必须运行完所有的前台线程才可以退出
- 后台线程:应用程序可以不考虑其是否已经运行完毕而直接退出,所有的后台线程在应用程序退出时都会自动结束
前台线程阻止进程的关闭
Thread thread = new Thread(() =>
{
Thread.Sleep();
Console.WriteLine("前台线程执行");
});
thread.Start();
Console.WriteLine("主线程执行完毕");
输出结果:
这里主线程马上执行完成,并不马上关闭,前台线程等待5秒再执行输出,然后控制台退出
后台线程不阻止进程的关闭
Thread thread = new Thread(() =>
{
Thread.Sleep();
Console.WriteLine("前台线程执行");
})
{ IsBackground = true }; thread.Start();
Console.WriteLine("主线程执行完毕");
输出结果:不等后台线程执行完毕,主线程执行完毕后立即退出,控制台立即退出
线程优先级
之前说到,线程是由操作系统调度的,给线程指定优先级,可以影响调度顺序,C#中Thread类的Priority属性提供了五种线程优先级别,这是一个枚举对象;默认为Normal
- Highest最高()
- AboseNormal(高于正常)
- Normal(正常)
- BelowNormal(低于正常)
- Lowest(最低)
static void Main(string[] args)
{
Thread normal = new Thread(() =>
{
Console.WriteLine("优先级为正常线程");
});
normal.Start(); Thread aboseNormal = new Thread(() =>
{
Console.WriteLine("优先级为高于正常线程");
})
{ Priority = ThreadPriority.AboveNormal };
aboseNormal.Start(); Thread belowNormal = new Thread(() =>
{
Console.WriteLine("优先级为低于正常线程");
})
{ Priority = ThreadPriority.BelowNormal };
belowNormal.Start(); Thread highest = new Thread(() =>
{
Console.WriteLine("优先级最高线程");
})
{ Priority = ThreadPriority.Highest };
highest.Start(); Thread lowest = new Thread(() =>
{
Console.WriteLine("优先级最低线程");
})
{ Priority = ThreadPriority.Lowest };
lowest.Start(); Console.WriteLine("主线程执行完毕");
Console.ReadKey();
}
结果可知:设置优先级并不是会指定线程固定执行的顺序,设置线程优先级只是提高了线程被调用的概率,并不是定义CPU调用线程的顺序,具体还是要由操作系统内部来调度
线程控制
Thread类常用的属性
CurrentThread | 获取当前正在运行的线程 |
IsAlive | 获取一个值,该值指示当前线程的执行状态 |
Name | 获取或设置线程的名称 |
Priority | 获取或设置一个值,该值指示线程的调度优先级 |
ThreadState | 获取一个值,该值包含当前线程的状态 |
Thread类常用的方法
Abort | 调用此方法通常会终止线程 |
Join | 阻止调用线程,直到某个线程终止时为止 |
Resume | 继续已挂起的线程 |
Sleep | 将当前线程阻止指定的毫秒数 |
Start | 使线程被安排进行执行 |
Suspend | 挂起线程,或者如果线程已挂起,则不起作用 |
暂停线程
static void Main(string[] args)
{
Thread t = new Thread(ThreadMethodSleep);
t.Start();
ThreadMethod();
Console.ReadKey();
} static void ThreadMethod()
{
for (int i = ; i < ; i++)
{
Console.WriteLine("a");
}
} static void ThreadMethodSleep()
{
for (int i = ; i < ; i++)
{
//暂停2s
Thread.Sleep(TimeSpan.FromSeconds());
Console.WriteLine("b");
}
}
工作原理
当程序运行时,主线程创建,而后创建线程t,该线程首先执行ThreadMethodSleep方法中的代码。然后会立即执行ThreadMethod方法。关键之处在于在ThreadMethodSleep方法中加入了Thread.Sleep方法调用。这将导致线程执行该代码时,在打印任何数字之前会等待指定的时间(本例中是2秒钟),当线程处于休眠状态时,它会占用尽可能少的CPU时间。结果发现通常后运行的ThreadMethod方法中的代码会比独立线程中的ThreadMethodSleep方法中的代码先执行。
线程等待(Thread.Join())
static void Main(string[] args)
{
Thread t = new Thread(ThreadMethodSleep);
t.Start();
t.Join();
ThreadMethod();
Console.ReadKey();
} static void ThreadMethod()
{
for (int i = ; i < ; i++)
{
Console.WriteLine("a");
}
} static void ThreadMethodSleep()
{
for (int i = ; i < ; i++)
{
//暂停2s
Thread.Sleep(TimeSpan.FromSeconds());
Console.WriteLine("b");
}
}
工作原理
程序运行后,创建线程t,调用ThreadMethodSleep()方法,该方法循环打印3个"b",但是每次打印前都要暂停2s,在主程序中调用t.join方法,该方法允许等待线程t完成,只有t线程完成后才会继续执行主程序的代码,这个例子中就是主线程等待t线程完成后再继续执行,主线程等待时处于阻塞状态
Suspend和Resume方法来同步线程
需要多线程编程时为了挂起与恢复线程可以使用Thread类的Suspend()与Resume()方法,可是VS提示这两个方法已经过时,原因MSDN:https://docs.microsoft.com/en-us/dotnet/api/system.threading.thread.suspend?redirectedfrom=MSDN&view=netframework-4.7.2#System_Threading_Thread_Suspend
终止线程(Thread.Abort())
static void Main(string[] args)
{
Thread t = new Thread(ThreadMethodSleep);
t.Start();
Thread.Sleep(TimeSpan.FromSeconds());
t.Abort();
Console.ReadKey();
} static void ThreadMethodSleep()
{
for (int i = ; i < ; i++)
{
//暂停2s
Thread.Sleep(TimeSpan.FromSeconds());
Console.WriteLine("b");
}
}
工作原理
程序运行后,创建线程t,调用ThreadMethodSleep()方法,该方法循环打印20个"b",但是每次打印前都要暂停2s,在主线程中设置等待6s后调用t.Abort()方法,这给线程注入了ThreadAbortException方法,导致线程被终结。这非常危险,因为该异常可以在任何时刻发生并可能彻底摧毁应用程序。另外,使用该技术也不一定总能终止线程。目标线程可以通过处理该异常并调用Thread.ResetAbort方法来拒绝被终止。因此并不推荐使用,Abort方法来关闭线程。可优先使用一些其他方法,比如提供一个CancellationToken方法来,取消线程的执行
线程的状态监测
通过Thread.ThreadState属性读取当前线程状态
static void Main(string[] args)
{
Thread t = new Thread(ThreadMethodSleep);
Console.WriteLine("创建线程,线程状态:{0}", t.ThreadState);
t.Start();
Console.WriteLine("线程调用Start()方法,线程状态:{0}", t.ThreadState);
Thread.Sleep(TimeSpan.FromSeconds());
Console.WriteLine("线程休眠5s,线程状态:{0}", t.ThreadState);
t.Join();
Console.WriteLine("等待线程结束,线程状态:{0}", t.ThreadState);
Console.ReadKey();
} static void ThreadMethodSleep()
{
for (int i = ; i < ; i++)
{
//暂停2s
Thread.Sleep(TimeSpan.FromSeconds());
Console.WriteLine("b");
}
}
工作原理
程序运行后,创建线程t(Unstarted)->调用t.start()方法(Running)->调用t.sleep()方法(WaitSleepJoin)->t.join()线程结束(Stopped)
异常处理
static void Main(string[] args)
{
Thread t = new Thread(ThreadMethodB);
t.Start();
t.Join();
try
{
Thread t1 = new Thread(ThreadMethodA);
t1.Start();
}
catch (Exception e)
{
Console.WriteLine("Error:{0}", e.Message);
}
Console.ReadKey();
} static void ThreadMethodA()
{
throw new Exception("AError");
}
static void ThreadMethodB()
{
try
{
throw new Exception("BError");
}
catch (Exception ex)
{
Console.WriteLine("Error:{0}", ex.Message);
}
}
工作原理
程序运行后,定义了两个会抛出异常的线程,其中一个对异常进行了处理,另一个没有,可以看到ThreadMethodA()方法中的异常没有被主程序包裹线程启动的try/catch代码块捕获到,所以如果直接使用线程,一般不要在线程中抛出异常,而在线程代码中使用try/catch代码块
线程池ThreadPool
创建线程需要时间.如果有不同的短任务要完成,就可以事先创建许多线程,在应完成这些任务时发出请求,这个线程数最好在需要更多的线程时增加,在需要释放资源时减少.C#中不需要自己创建维护这样一个列表,该列表由ThreadPool类托管.该类会在需要时增减池中线程的线程数,直到最大线程数,线程数的值是可配置的,如果线程池中个数到达了设置的极限,还是有更多的作业要处理,最新的作业就要排队,且必须等待线程完成其任务
class ThreadTest
{
static void Main(string[] args)
{
int workThread;
int ioThread;
ThreadPool.GetMaxThreads(out workThread, out ioThread);
Console.WriteLine("工作线程数:{0},io线程数{1}", workThread, ioThread); for (int i = ; i < ; i++)
{
ThreadPool.QueueUserWorkItem(AddThread);
workThread = ioThread = ;
}
Console.ReadKey();
} public static void AddThread(object e)
{
Console.WriteLine("当前线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
}
}
ThreadPool.GetMaxThreads(out workThread, out ioThread)接收两个out int类型参数返回最大工作线程数和io线程数,for循环中使用ThreadPool.QueueUserWorkItem()方法传递WaitCallback类型委托,将AddThread()方法赋予线程池中的线程,线程池收到请求后,如果线程池还没有运行,就会创建一个线程池,并启动第一个线程,如果已经启动,且有一个空闲线程来完成任务,就把该任务传递给这个线程
线程池使用限制
1:线程池中所有线程都是后台线程,如果进程的所有前台线程都结束了,所有的后台线程就会停止,不能把入池的线程改为前台线程
2:不能给入池的线程设置优先级或名称
3:入池的线程只能用于时间较短的任务,如果线程要一直运行,就应使用Thread类创建一个线程
4:对于COM对象,入池的所有线程都是多线程单元(MTA)线程,许多COM对象都需要单线程单元(STA)
多线程Thread的更多相关文章
- c#中@标志的作用 C#通过序列化实现深表复制 细说并发编程-TPL 大数据量下DataTable To List效率对比 【转载】C#工具类:实现文件操作File的工具类 异步多线程 Async .net 多线程 Thread ThreadPool Task .Net 反射学习
c#中@标志的作用 参考微软官方文档-特殊字符@,地址 https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/toke ...
- 2.匿名类,匿名类对象,private/protected/public关键字、abstract抽象类,抽象方法、final关键字的使用,多线程Thread类start方法原理
package com.bawei.multithread; //注意:模板方法我们通常使用抽象类或者抽象方法!这里我们为了方便在本类中使用就没有使用抽象类/抽象方法 public class Tem ...
- 多线程-Thread和ThreadPool
多线程原理 多线程都是基于委托的. 多线程优缺点 缺点: 1.导致程序复杂,开发调试维护困难,特别是线程交互. 2.线程过多导致服务器卡死,因为占用CPU 内存等资源. 优点: 1.良好的交互,特别对 ...
- Java多线程Thread
转自:http://www.cnblogs.com/lwbqqyumidi/p/3804883.html Java总结篇系列:Java多线程(一) 多线程作为Java中很重要的一个知识点,在此还是 ...
- java 多线程--- Thread Runnable Executors
java 实现多线程的整理: Thread实现多线程的两种方式: (1)继承 Thread类,同时重载 run 方法: class PrimeThread extends Thread { long ...
- python进阶学习笔记(四)--多线程thread
在使用多线程之前,我们首页要理解什么是进程和线程. 什么是进程? 计算机程序只不过是磁盘中可执行的,二进制(或其它类型)的数据.它们只有在被读取到内存中,被操作系统调用的时候才开始它们的生命期.进程( ...
- php多线程thread开发与应用的例子
Php多线程的使用,首先需要PHP5.3以上版本,并安装pthreads PHP扩展,可以使PHP真正的支持多线程,扩展如何安装请自行百度 PHP扩展下载:https://github.com/kra ...
- 多线程(Thread),其实很简单!
目录: 1:线程简介 2:怎么操作线程 3:Thread的常用方法 4:简单的获奖机 5:应用程序域 线程:是Windows任务调度的最小单位.线程是程序中的一个执行流,每个线 ...
- 【C#多线程】C#多线程 Thread 开发基础
引用 using System; using System.Threading; 多线程代码 Thread mainthread = new Thread(ExecuteThread); mainth ...
- android 多线程Thread,Runnable,Handler,AsyncTask
先看两个链接: 1.http://www.2cto.com/kf/201404/290494.html 2. 链接1: android 的多线程实际上就是java的多线程.android的UI线程又称 ...
随机推荐
- Linux下逻辑地址、线性地址、物理地址详细总结
Linux下逻辑地址.线性地址.物理地址详细总结 一.逻辑地址转线性地址 机器语言指令中出现的内存地址,都是逻辑地址,需要转换成线性地址,再经过MMU(CPU中的内存管理单元)转换成物理地址 ...
- SVN:This client is too old to work with working copy…解决的方法
解决svn:This client is too old问题 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbXlmbXlmbXlmbXlm/font/5a ...
- Metadata GC Threshold导致的full gc分析
gc log 两次full gc 均是Metadata GC导致, OpenJDK -Bit Server VM ( :: by (Red Hat -) Memory: 4k page, physic ...
- CDC在sql server 2017中无法使用的问题
Symptom === sp_MScdc_capture_job in the CDC job raised error message Msg 217, Level 16, State 1, Pro ...
- vux安装中遇到的坑(转)
1.输入 npm install vux --save 2.输入 npm install vux-loader --save-dev(没安装的时候,会一直报错) 3.build/webpack.bas ...
- Linux服务器重启后eureka报错
在Linux服务器重启后,首次启动应用时查看eureka注册中心,报错 EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP W ...
- Sword libcurl回调函数相关知识
libcurl响应回调函数说明 libcurl在默认情况下,回调里面会将数据分段的返回,不会一下子将发送端的数据全部塞到回调函数里面, 经过源码分析回调函数和curl_easy_perform是在 ...
- 框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习)
一.AOP的核心概念回顾 https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#a ...
- chmod chown
chmod以及chown,其中可以用递归参数-R来实现更改所有子文件和子目录的权限. 1.利用chmod修改权限: 对Document/目录下的所有子文件与子目录执行相同的权限变更: chmod -R ...
- 异常:Project configuration is not up-to-date with pom.xml 解决方案
错误描述,在导入Maven项目后出现下面错误: Description Resource Path Location Type Project configuration is not up-to-d ...