深入线程

前言                                      

  在校时认识的线程就是获取CPU执行时间的最小单位,多个线程共享所在进程的资源和内存空间,偶然会听说线程拥有上下文这一概念,但没有深入了
解学习,如今工作一年多后顿悟要及时补回这方面的知识于是参考各大哥们所分享的资料,学习、总结一下自己对线程的理解,本篇内容主要从原理、使用上记录讲
解线程相关知识,其中若有谬误请各位多多指正,并该篇会随自身对线程的理解不断的修改扩充,多谢关注。

     主要参考:.net 4.0 学习笔记(3)—— 线程基础(上)

        .net 4.0 学习笔记(4)—— 线程基础(下) 

初识线程                                  

  参考:http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=231910

     线程是进程内部的“轻量级”执行单位(比进程便宜但比分配其他资源昂贵),多个线程共享进程内的代码段和数据段,每个线程拥有独立的程序计数器、寄存器和堆栈,共享全局变量和静态变量。

  现阶段.net中CLR的线程是一一映射到Windows操作系统的物理线程(Jeffrey
Richter预言.net迟早会实现CLR的逻辑线程多对一映射到Windows操作系统的物理/本地线程,降低多线程的开销)。所以下面让我们从
Windows操作系统的线程开始说起吧

 

Windows线程                               

1.线程含有的元素:

  a.线程内核对象(Thread Kernel):操作系统分配给线程的数据结构,该数据结构用于描述线程的属性,其中包含线程上下文。

  线程上下文包括:安全上下文、同步上下文(System.Threading.SynchronizationContext)、本地化上下
文、调用上下文(System.Threading.ExecutionContext)、事务上下文和CLR宿主上下文
(System.Threading.HostExecutionContext)。

  执行上下文:是某一个时刻的线程上下文,如进程是执行中的程序那样,执行中的线程上下文就是执行上下文。通过System.Threading.ExecutionContext获取,。

  线程调度时上一个线程的CPU寄存器的状态会保存到该线程上下文中。如果两个线程属于同一个进程时,会根据是否压抑执行上下文流动来决定是否复制上一个线程的上下文到要执行的线程的上下文中(具体请留意上下文的流动);如果不属于同一个进程,就要在执行代码和访问数据前改变虚拟内存地址。然后加载要执行的线程的上下文到CPU寄存器并执行线程。

 

  b.线程环境块
(Thread Environment
Block):用于在用户模式(应用程序能快速访问的地址空间)分配和初始化一个内存块,消耗1个内存页(4KB在x86和x64
CPU上,8KB在IA64
CPU上)。其中还包含一个线程异常处理链头,当线程进入try语句块时就会将一个节点插入到该链头,退出try语句块时就从立案头移除该节点。

  c.用户模式堆栈(User Mode Stack):用于保存方法的局部变量、参数和方法返回时继续执行的地址。Windows默认分配1MB给用户模式堆栈。

  d.内核模式堆栈(Kernel Mode Stack):windows默认分配12KB(x86系统)或24KB(x64系统)。

  主要有2个功能:一、当应用程序调用内核功能时,会将用户模式堆栈中的参数复制到内核模式堆栈,复制成功后内核会核实参数的值,而因为应用程序
不能访问内核模式堆栈,所以在参数在核实后无法被修改,从而保证内核功能被安全地调用;二、用于保存内核方法的局部变量、参数和方法返回后继续执行的地
址。

 

  e.DLL线程加载和卸载通知:请参考.net 4.0 学习笔记(3)—— 线程基础(上),因托管DLL不存在该情况,这里暂不讨论^_^。

 

CLR线程、操作系统线程和CPU关系                    

下图是三者的关系(单核CPU)

图上的进程均是CLR的托管进程,其中的线程为CLR的托管线程。一个托管进程对应一个本地进程,一个托管线程对应一个本地线程。

 

1.杀死其他进程:可以通过System.Diagnostics.Process.GetProcessByID方法根据PID(进程ID)获取某一正在运行的进程,然后调用该进程的CloseMainWindow或Kill方法杀死该进程。但不能杀死自己启动的进程,原因有待研究。。。。希望大哥们讲解一下

CloseMainWindow方法并不是强行杀死进程,而是如用户点击程序的关闭按钮一样关闭进程,所以可以在程序关闭事件中作处理操作甚至阻止
关闭进程的操作;而Kill是强行杀死进程,程序没有机会执行任何善后工作;而Close方法如dispose方法只是释放资源而没有杀死进程。

参考:【我的项目经验】——进程(下)

杀死进程Code:

int PID = 123;
Process p = Process.GetProcessById(PID);
if(!p.CloseMainWindow())
{
   p.Kill();
}

启动进程Code:

1 ProcessStartInfo psi = new ProcessStartInfo(@"C:\Program Files\KWMUSIC\KwMusic.exe");
2 Process d = Process.Start(psi);

上述操作均是通过本地线程执行。

2.GC执行时所有CLR线程均挂起,执行完后CLR线程会恢复继续等待调度。越多线程该过程越低效。

题外内容:

进程特点:

1.进程拥有独立的内存地址空间,一般情况包含:文本区域(text region),用于存储处理器执行的代码;数据区域(data region),用于存储变量和进程执行期间动态分配的内存;堆栈(stack),用于存储进程执行期间调用的指令和本地变量。

2.进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时,它才能成为一个活动的实体,我们称其为进程。

前台线程与后台线程                               

  前台、后台线程是CLR中的概念,CLR认为线程要不是前台线程就一定为后台线程。当进程中的所有前台线程都结束后,CLR会强制结束所有后台
线程,并且不会抛出异常,最后退出进程释放所有资源。线程池的线程默认为后台线程,而使用Thread生成的线程默认为前台线程,可以设置其实例
IsBackground属性设为后台线程。

 

线程优先级                                   

  线程的优先级从0(低)到31(高),但CLR不允许开发者直接设置这些优先级,而是提供7种优先级方便开发者操作。注意,线程的优先级并不是
单独起作用的,而是与所属进程的优先级关联起来后决定线程的真实优先级(进程拥有6种优先级)。进程优先级与线程优先级关联后线程优先级如下图:

Windows被称作抢先式多线程操作系统,其线程调度过程如下:

  1.系统搜寻最高优先级的线程并将其分配到CPU执行;

  2.当某个线程正被CPU执行时,有一个优先级更高的线程加入到队列中,那么就会挂起正在执行的线程而将优先级更高的线程分配到CPU。

创建线程                                  

  有两种方法:一、使用线程池(System.Threading.ThreadPool,推荐使用);二、使用Thread对象。

  一、线程池:

  使用ThreadPool.QueueUserWorkItem(WaitCallBack callBack)方法从线程池获取空闲线程执行代码,线程池中的线程的优先级均为Normal,默认为后台线程。

     二、使用Thread对象,默认为前台线程:

1 Thread t = new Thread(ParameterizedThreadStart start);//构造逻辑线程
2 t.Start(object state);//生成物理线程(实际对应一个操作系统的线程)

当符合以下条件时就用Thread对象吧,否则还是用线程池较好。

1.你需要线程允许一个非普通优先级。所有的线程池线程都允许在普通优先级。当然,这你可以改变,但是不推荐,在线程池操作过程中,优先级的改变不会持续。

2.你需要线程作为前台线程运转,从而防止程序终止一直到线程完成任务。线程池线程总是后台线程,如果CLR决定终止进程它们就不会完成任务。

3.受计算限制的任务需要时间非常长;这样,我不会让线程池负担逻辑,因为它试图找出是否需要创建一个额外的线程。

4.我想开始线程并很可能用Thread.Abort方法来过早的结束它。

 

线程上下文流动                                 

  参考:如何阻止线程执行上下文的传递

    线程上下文流动发生在线程调度时,当前后线程属于同一进程并没有阻止上下文流动时,在下一个线程执行代码和访问数据前将前一个线程的上下文会复制到下一个线程的上下文中,然后执行下一个线程。作用:这里打个比方,如单点登录系统那样在一个网站登录后浏览其他系统中的其他网站由于登录验证信息已经共享所以不需要重新登录验证,而线程上下文的复制就如登录验证信息共享那样。

   因上下文流动会降低一定的性能,所以在需要的情况下可以阻止上下文流动(如下一个线程执行不需要上一个线程的上下文信息)。下面提供两种方法实现阻止上下文流动:1,使用线程池System.Threading.ThreadPool.UnsafeQueueUserWorkItem方法调用另一个线程来执行代码;2,使用System.Threading.ExecutionContext.SuppressFlow方法来阻止当前线程将上下文复制到下一个线程的上下文。具体例子请参考:如何阻止线程执行上下文的传递

时间: 2024-11-03 15:28:21

深入线程的相关文章

新人一枚,请问怎样从B线程调用A线程的内容?

问题描述 新人一枚,请问怎样从B线程调用A线程的内容? 具体要求:写一个线程A,得到0-10的随机数,再写一个线程B来了调用线程A!!!!! 解决方案 int a;//全局变量,如果多个线程同步访问需要加锁 A...//线程A B...//线程B 代码结构大致如上,访问的时候,因为变量a是全局数据,所以在线程A和B中都可以直接访问,就好像C中的任何函数都可以访问和使用全局变量一样 解决方案二: 放是全局变量来交换数据

并发集合(三)使用阻塞线程安全的列表

使用阻塞线程安全的列表 列表(list)是最基本的集合.一个列表中的元素数量是不确定的,并且你可以添加.读取和删除任意位置上的元素.并发列表允许不同的线程在同一时刻对列表里的元素进行添加或删除,而不会产生任何数据不一致的问题. 在这个指南中,你将学习如何在你的并发应用程序中使用阻塞的列表.阻塞列表与非阻塞列表的主要区别是,阻塞列表有添加和删除元素的方法,如果由于列表已满或为空而导致这些操作不能立即进行,它们将阻塞调用的线程,直到这些操作可以进行.Java包含实现阻塞列表的LinkedBlocki

[转载]Linux 线程实现机制分析

  自从多线程编程的概念出现在 Linux 中以来,Linux 多线应用的发展总是与两个问题脱不开干系:兼容性.效率.本文从线程模型入手,通过分析目前 Linux 平台上最流行的 LinuxThreads 线程库的实现及其不足,描述了 Linux 社区是如何看待和解决兼容性和效率这两个问题的.   一.基础知识:线程和进程 按照教科书上的定义,进程是资源管理的最小单位,线程是程序执行的最小单位.在操作系统设计上,从进程演化出线程,最主要的目的就是更好的支持SMP以及减小(进程/线程)上下文切换开

你真的了解:IIS连接数、IIS并发连接数、IIS最大并发工作线程数、应用程序池的队列长度、应用程序池的最大工作进程数 吗?

原文:你真的了解:IIS连接数.IIS并发连接数.IIS最大并发工作线程数.应用程序池的队列长度.应用程序池的最大工作进程数 吗? IIS连接数   一般购买过虚拟主机的朋友都熟悉购买时,会限制IIS连接数,这边先从普通不懂代码用户角度理解IIS连接数 顾名思义即为IIS服务器可以同时容纳客户请求的最高连接数,准确的说应该叫"IIS限制连接数" 这边客户请求的连接内容包括: 1.网站html请求,html中的图片资源,html中的脚本资源,其他需要连接下载的资源等等,任何一个资源的请求

我的代码是怎么进入死循环的,为什么有两个线程运行一会就唤不醒了

问题描述 classResource{privateStringname;privateintcount=1;privatebooleanflag=false;publicsynchronizedvoidset(Stringname){while(flag){notify();try{wait();}catch(InterruptedExceptione){}System.out.println(Thread.currentThread().getName()+"....循环唤醒..."

线程基础之JAVA和C++0x的特性

译文连接   译文地址  译者:衣着时   校对:丁一    (有兴趣参与试译或校对的同学,请加入并发网试译者QQ群:369468545) JAVA特性 JAVA线程通常是一个带有run()方法的java.lang.Thread的子类,然后调用这个子类对象的start()方法.我们之前定义过,数据竞争是因为两个线程同时访问内存单元,在JAVA中,内存单元是一个对象字段或数组元素. 由于JAVA旨在支持运行不受信任代码作为受信任的应用程序的一部分,必须限制不受信任代码的数据争用造成的破坏.因此不允

ios-在背景线程中处理数据

问题描述 在背景线程中处理数据 我写了一方法如下: - (BOOL)shouldDoSomeWork { BOOL result = // here I need do hard work with data in background thread and return result so main thread should wait until the data is calculated and then return result; return result;} 如何实现? 解决方案

线程同步 卖票问题 三个车站卖七张票

问题描述 线程同步 卖票问题 三个车站卖七张票 正确的写法:public class mainclass { public static void main(String[] args) { Train A=new Train(); Train B=new Train(); Train C=new Train(); A.start(); B.start(); C.start(); } } class Train extends Thread{ public static int ticket=7

在非UI线程处理Bitmap

http://my.oschina.net/ryanhoo/blog/88344 译者:Ryan Hoo 来源:https://developer.android.com/develop/index.html 译者按: 在Google最新的文档中,提供了一系列含金量相当高的教程.因为种种原因而鲜为人知,真是可惜!Ryan将会细心整理,将之翻译成中文,希望对开发者有所帮助.         本系列是Google关于展示大Bitmap(位图)的官方演示,可以有效的解决内存限制,更加有效的加载并显示图

基本线程同步(四)在同步代码中使用条件

在同步代码中使用条件 在并发编程中的一个经典问题是生产者与消费者问题,我们有一个数据缓冲区,一个或多个数据的生产者在缓冲区存储数据,而一个或多个数据的消费者,把数据从缓冲区取出. 由于缓冲区是一个共享的数据结构,我们必须采用同步机制,比如synchronized关键字来控制对它的访问.但是我们有更多的限制因素,如果缓冲区是满的,生产者不能存储数据,如果缓冲区是空的,消费者不能取出数据. 对于这些类型的情况,Java在Object对象中提供wait(),notify(),和notifyAll()