Thread类(线程)
操作系统通过线程对程序的执行进行管理,当操作系统运行一个程序的时候,首先,操作系统将为这个准备运行的程序分配一个进程,以管理这个程序所需要的各种资源。在这些资源之中,会包含一个称为主线程的线程数据结构,用来管理这个程序的执行状态。
在Windows操作系统下,线程的的数据结构包含以下内容:
1、线程的核心对象:主要包含线程当前的寄存器状态,当操作系统调度这个线程开始运行的时候,寄存器的状态将被加载到CPU中,重新构建线程的执行环境,当线程被调度出来的时候,最后的寄存器状态被重新保存到这里,已备下一次执行的时候使用。
2、线程环境块(Thread Environment Block,TED):是一块用户模式下的内存,包含线程的异常处理链的头部。另外,线程的局部存储数据(Thread Local Storage Data)也存在这里。
3、用户模式的堆栈:用户程序的局部变量和参数传递所使用的堆栈,默认情况下,Windows将会被分配1M的空间用于用户模式堆栈。
4、内核模式堆栈:用于访问操作系统时使用的堆栈。
在抢先式多任务的环境下,在一个特定的时间,CPU将一个线程调度进CPU中执行,这个线程最多将会运行一个时间片的时间长度,当时间片到期之后,操作系统将这个线程调度出CPU,将另外一个线程调度进CPU,我们通常称这种操作为上下文切换。
在每一次的上下文切换时,Windows将执行下面的步骤:
- 将当前的CPU寄存器的值保存到当前运行的线程数据结构中,即其中的线程核心对象中。
- 选中下一个准备运行的线程,如果这个线程处于不同的进程中,那么,还必须首先切换虚拟地址空间。
- 加载准备运行线程的CPU寄存器状态到CPU中。
公共语言运行时CLR(Common Language Runtime)是.Net程序运行的环境,它负责资源管理,并保证应用和底层操作系统之间必要的分离。
在.Net环境下,CLR中的线程需要通过操作系统的线程完成实际的工作,目前情况下,.Net直接将CLR中的线程映射到操作系统的线程进行处理和调度,所以,我们每创建一个线程将会消耗1M以上的内存空间。但未来CLR中的线程并不一定与操作系统中的线程完全对应。通过创建CLR环境下的逻辑线程,我们可能创建更加节省资源的线程,使得大量的CLR线程可以工作在少量的操作系统线程之上。
1. System.Threading.Thread类
System.Threading.Thread是用于控制线程的基础类,通过Thread可以控制当前应用程序域中线程的创建、挂起、停止、销毁。
它包括以下常用公共属性:
| 属性名称 | 说明 |
|---|---|
| CurrentContext | 获取线程正在其中执行的当前上下文。 |
| CurrentThread | 获取当前正在运行的线程。 |
| ExecutionContext | 获取一个 ExecutionContext 对象,该对象包含有关当前线程的各种上下文的信息。 |
| IsAlive | 获取一个值,该值指示当前线程的执行状态。 |
| IsBackground | 获取或设置一个值,该值指示某个线程是否为后台线程。 |
| IsThreadPoolThread | 获取一个值,该值指示线程是否属于托管线程池。 |
| ManagedThreadId | 获取当前托管线程的唯一标识符。 |
| Name | 获取或设置线程的名称。 |
| Priority | 获取或设置一个值,该值指示线程的调度优先级。 |
| ThreadState | 获取一个值,该值包含当前线程的状态。 |
常用属性示例:
using System;
using System.Threading; namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
//新建3个线程并设定各自的优先级
Thread t1 = new Thread(Run);
t1.Priority = ThreadPriority.Normal;
t1.Start(); Console.ReadKey();
} public static void Run()
{
Thread t1 = Thread.CurrentThread; //静态属性,获取当前执行这行代码的线程
Console.WriteLine("我的优先级是:" + t1.Priority);
Console.WriteLine("我是否还在执行:" + t1.IsAlive);
Console.WriteLine("是否是后台线程:" + t1.IsBackground);
Console.WriteLine("是否是线程池线程:" + t1.IsThreadPoolThread);
Console.WriteLine("线程唯一标识符:" + t1.ManagedThreadId);
Console.WriteLine("我的名称是:" + t1.Name);
Console.WriteLine("我的状态是:" + t1.ThreadState);
}
}
}
2. 线程的标识符
ManagedThreadId是确认线程的唯一标识符,程序在大部分情况下都是通过Thread.ManagedThreadId来辨别线程的。而Name是一个可变值,在默认时候,Name为一个空值 Null,开发人员可以通过程序设置线程的名称,但这只是一个辅助功能。
3. 线程的优先级别
.NET为线程设置了Priority属性来定义线程执行的优先级别,里面包含5个选项,其中Normal是默认值。除非系统有特殊要求,否则不应该随便设置线程的优先级别。
| 成员名称 | 说明 |
|---|---|
| Lowest | 可以将 Thread 安排在具有任何其他优先级的线程之后。 |
| BelowNormal | 可以将 Thread 安排在具有 Normal 优先级的线程之后,在具有 Lowest 优先级的线程之前。 |
| Normal | 默认选择。可以将 Thread 安排在具有 AboveNormal 优先级的线程之后,在具有BelowNormal 优先级的线程之前。 |
| AboveNormal | 可以将 Thread 安排在具有 Highest 优先级的线程之后,在具有 Normal 优先级的线程之前。 |
| Highest | 可以将 Thread 安排在具有任何其他优先级的线程之前。 |
优先级的示例:
using System;
using System.Threading; namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
//新建3个线程并设定各自的优先级
Thread t1 = new Thread(Run);
t1.Priority = ThreadPriority.Lowest;
Thread t2 = new Thread(Run);
t2.Priority = ThreadPriority.Normal;
Thread t3 = new Thread(Run);
t3.Priority = ThreadPriority.Highest;
//由低到高优先级的顺序依次调用
t1.Start();
t2.Start();
t3.Start();
Console.ReadKey();
} public static void Run()
{
Console.WriteLine("我的优先级是:" + Thread.CurrentThread.Priority);
}
}
}
4. 线程的状态
通过ThreadState可以检测线程是处于Unstarted、Sleeping、Running 等等状态,它比 IsAlive 属性能提供更多的特定信息。
前面说过,一个应用程序域中可能包括多个上下文,而通过CurrentContext可以获取线程当前的上下文。
CurrentThread是最常用的一个属性,它是用于获取当前运行的线程。
5. System.Threading.Thread的方法
Thread 中包括了多个方法来控制线程的创建、挂起、停止、销毁,以后来的例子中会经常使用。
| 方法名称 | 说明 |
|---|---|
| Abort() | 终止本线程。 |
| GetDomain() | 返回当前线程正在其中运行的当前域。 |
| GetDomainId() | 返回当前线程正在其中运行的当前域Id。 |
| Interrupt() | 中断处于 WaitSleepJoin 线程状态的线程。 |
| Join() | 已重载。 阻塞调用线程,直到某个线程终止时为止。 |
| Resume() | 继续运行已挂起的线程。 |
| Start() | 执行本线程。 |
| Suspend() | 挂起当前线程,如果当前线程已属于挂起状态则此不起作用 |
| Sleep() | 把正在运行的线程挂起一段时间。 |
6. 开发实例
前台线程与后台线程的区别
我们看到上面有个属性叫后台线程,非后台线程就叫前台线程吧,Thread.Start()启动的线程默认为前台线程,启动程序时创建的主线程一定是前台线程。应用程序与必须等到所有的前台线程执行完毕才会卸载。而当IsBackground设置为true时,就是后台线程了,当主线程执行完毕后就直接卸载,不再理会后台线程是否执行完毕。
前台与后台线程的设置必须在线程启动之前进行设置,线程启动之后就不能设置了。
Thread创建的线程是前台线程,线程池中的是后台线程。
using System;
using System.Threading; namespace ConsoleApp1
{
public delegate string MethodCaller(string name);//定义个代理 class Program
{
static void CountNumbers()
{
int _iterations = ;
for (int i = ; i <= _iterations; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(0.5));
Console.WriteLine("{0} prints {1}", Thread.CurrentThread.Name, i);
}
} static void Count(object iterations)
{
CountNumbers((int)iterations);
} static void CountNumbers(int iterations)
{
for (int i = ; i <= iterations; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(0.5));
Console.WriteLine("{0} prints {1}", Thread.CurrentThread.Name, i);
}
} static void PrintNumber(int number)
{
Console.WriteLine(number);
} static string GetName(string name) // 函数
{
int _iterations = ;
for (int i = ; i <= _iterations; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(0.5));
Console.WriteLine("{0} prints {1}", Thread.CurrentThread.Name, i);
}
return name;
} static void Main(string[] args)
{
//无参数的调用
var threadOne = new Thread(CountNumbers);
threadOne.Name = "ThreadOne";
threadOne.Start();
threadOne.Join();
Console.WriteLine("--------------------------"); //带参数的调用方式一
var threadTwo = new Thread(Count);
threadTwo.Name = "ThreadTwo";
threadTwo.Start();
threadTwo.Join();
Console.WriteLine("--------------------------"); //带参数的调用方式二
var threadThree = new Thread(() => CountNumbers());
threadThree.Name = "ThreadThree";
threadThree.Start();
threadThree.Join();
Console.WriteLine("--------------------------"); //注意在多个lambda表达式中使用香港的变量,它们会共享该变量 这里两个线程的参数都是20
int i = ;
var threadFour = new Thread(() => PrintNumber(i));
i = ;
var threadFive = new Thread(() => PrintNumber(i));
threadFour.Start();
threadFive.Start();
Console.WriteLine("--------------------------"); MethodCaller mc = new MethodCaller(GetName);
string name = "my name";//输入参数
IAsyncResult result = mc.BeginInvoke(name, null, null);
Console.WriteLine("继续主线程的业务");
string myname = mc.EndInvoke(result);//用于接收返回值
Console.WriteLine(string.Format("result={0}", myname));
}
}
}
2、等待线程结束,会阻塞当前主线程(.Join)
using System;
using System.Threading; namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Starting program...");
Thread t = new Thread(PrintNumbersWithDelay);
t.Start();
t.Join();
Console.WriteLine("Thread completed");
} static void PrintNumbersWithDelay()
{
Console.WriteLine("Starting...");
for (int i = ; i <= ; i++)
{
Thread.Sleep(TimeSpan.FromSeconds());
Console.WriteLine(i);
}
}
}
}
3、线程中异常的处理只能在线程调用中的函数里面去处理,在外面是接收不到线程中的报错的
using System;
using System.Threading; namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var t = new Thread(FaultyThread);
t.Start();
t.Join(); //try
//{
// t = new Thread(BadFaultyThread);
// t.Start();
//}
//catch (Exception ex)
//{
// //这里捕获不了线程中的异常
// Console.WriteLine("We won't get here!");
//}
} static void BadFaultyThread()
{
Console.WriteLine("Starting a Badfaulty thread...");
Thread.Sleep(TimeSpan.FromSeconds());
throw new Exception("Boom!");
} static void FaultyThread()
{
try
{
Console.WriteLine("Starting a faulty thread...");
Thread.Sleep(TimeSpan.FromSeconds());
throw new Exception("Boom!");
}
catch (Exception ex)
{
Console.WriteLine("Exception handled: {0}", ex.Message);
}
}
}
}
4、设置线程优先级(.Priority)
using System;
using System.Diagnostics;
using System.Threading; namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Current thread priority: {0}", Thread.CurrentThread.Priority);
Console.WriteLine("Running on all cores available");
RunThreads();
Thread.Sleep(TimeSpan.FromSeconds());
Console.WriteLine("Running on a single core");
Process.GetCurrentProcess().ProcessorAffinity = new IntPtr();
RunThreads();
Console.ReadLine();
} static void RunThreads()
{
var sample = new ThreadSample(); var threadOne = new Thread(sample.CountNumbers);
threadOne.Name = "ThreadOne";
var threadTwo = new Thread(sample.CountNumbers);
threadTwo.Name = "ThreadTwo"; threadOne.Priority = ThreadPriority.Lowest;
threadTwo.Priority = ThreadPriority.Highest;
threadOne.Start();
threadTwo.Start(); Thread.Sleep(TimeSpan.FromSeconds());
sample.Stop();
} class ThreadSample
{
private bool _isStopped = false; public void Stop()
{
_isStopped = true;
} public void CountNumbers()
{
long counter = ; while (!_isStopped)
{
counter++;
} Console.WriteLine("{0} with {1,11} priority " +
"has a count = {2,13}", Thread.CurrentThread.Name,
Thread.CurrentThread.Priority,
counter.ToString("N0"));
}
}
}
}
Thread类(线程)的更多相关文章
- Thread类线程结束会唤醒使用其对象做锁而睡眠的线程
首先回顾一下我们的基础知识. sleep: 线程睡眠,不会释放锁 wait: 线程等待.释放锁. notity: 唤醒随机一个当前对象等待的线程,并不会释放锁 notityAll: 唤醒所有当前对象等 ...
- C# Thread类 线程优先级
1.C#对线程进行操作时,通过Thread类,可以对线程进行创建.挂起.恢复.休眠.终止及设置优先级. Thread类位于System.Threading命名空间下,该命名空间还包含一个ThreadP ...
- Java基础-多线程编程-1.随便选择两个城市作为预选旅游目标。实现两个独立的线程分别显示10次城市名,每次显示后休眠一段随机时间(1000ms以内),哪个先显示完毕,就决定去哪个城市。分别用Runnable接口和Thread类实现。
1.随便选择两个城市作为预选旅游目标.实现两个独立的线程分别显示10次城市名,每次显示后休眠一段随机时间(1000ms以内),哪个先显示完毕,就决定去哪个城市.分别用Runnable接口和Thread ...
- Android线程管理(三)——Thread类的内部原理、休眠及唤醒
线程通信.ActivityThread及Thread类是理解Android线程管理的关键. 线程,作为CPU调度资源的基本单位,在Android等针对嵌入式设备的操作系统中,有着非常重要和基础的作用. ...
- C#线程系列讲座(2):Thread类的应用
一.Thread类的基本用法 通过System.Threading.Thread类可以开始新的线程,并在线程堆栈中运行静态或实例方法.可以通过Thread类的的构造方法传递一个无参数,并且不返回值(返 ...
- Java基础之线程——派生自Thread类的子类(TryThread)
控制台程序. 程序总是至少有一个线程,程序开始执行时就会创建这个线程.在普通的Java应用程序中,这个线程从mian()方法的开头启动. 要开始执行线程,可以调用Thread对象的start()方法. ...
- 【转】线程、Thread类和线程终止
一.线程Thread启动 0. Thread类实现了java.lang.Runnable接口,即实现了run方法.虽然在Sun JDK中,start()调用了start0()方法,start0()方法 ...
- 线程入门之实现Runnable接口和继承Thread类
线程的2种使用方式:实现Runnable接口和继承Thread类 1.实现Runnable接口 实现Runnable接口,必须实现run方法,也是Runnable接口中的唯一一个方法 class Ru ...
- 使用Thread类可以创建和控制线程
1.创建线程 static void Main(string[] args) { /* Thread类 * 创建控制线程 * 其构造函数接受ThreadStart和ParameterizedThrea ...
随机推荐
- 洛谷P1776--宝物筛选(单调队列+多重背包)
https://www.luogu.org/problemnew/show/P1776 单调队列+多重背包的讲解https://www.cnblogs.com/JoeFan/p/4165956.htm ...
- OC中NSString的使用、字符串的使用
字符串作为OC语言的基本对象类型,被我们在各种地方广泛使用,因此熟悉掌握字符串的属性和方法,是OC必不可少的基础之一. 字符串分为可变字符串(NSMutableString)和不可变字符串(NSStr ...
- 解压->静态库.a文件
1. cd /Volumes/HHD/PQS/apple/Public 2. file com_PQS.a com_PQS.a: Mach-O universal binary with 5 arch ...
- 《python语言程序设计》_第二章笔记
#2.2_编写一个简单的程序 项目1: 设计:radius=20,求面积area? 程序: radius=20 #给变量radius复制area=radius*radius*3.14159 #编写ar ...
- Python爬虫的学习经历
在准备学习人工智能之前呢,我看了一下大体的学习纲领.发现排在前面的是PYTHON的基础知识和爬虫相关的知识,再者就是相关的数学算法与金融分析.不过想来也是,如果想进行大量的数据运算与分析,宏大的基础数 ...
- 怎么取cxgrid某一列的合计值
怎么取cxgrid某一列的合计值 1.cxGrid1DBTableView1->optionsview->Footer 设为True 2.cxGrid1DBTableView1-> ...
- Redis-04.备份与恢复
RDB(Redis DataBase) 在指定的时间间隔内将内存中的数据集快照写入磁盘,可以理解为Snapshot快照,它恢复时是将快照文件直接读到内存里. Redis会单独创建(fork)一个子进程 ...
- VSCode插件开发全攻略(七)WebView
更多文章请戳VSCode插件开发全攻略系列目录导航. 什么是Webview 大家都知道,整个VSCode编辑器就是一张大的网页,其实,我们还可以在Visual Studio Code中创建完全自定义的 ...
- 110道python面试笔试题汇总,你能答对几道?
数据分析系列教程插播一篇面试题教程,最近公众号新来了不少朋友,这几天不少粉丝留言说问我有没有python面试题,其实之前分享过一些面试题,今天统一再给大家分享一遍,也希望能帮助此时仍在找工作的同学,尽 ...
- Metasploit Framework(3)Meterpreter
文章的格式也许不是很好看,也没有什么合理的顺序 完全是想到什么写一些什么,但各个方面都涵盖到了 能耐下心看的朋友欢迎一起学习,大牛和杠精们请绕道 Meterpreter:是一种高级的,动态的,可拓展的 ...