可视化程序设计.多线程技术_第1页
可视化程序设计.多线程技术_第2页
可视化程序设计.多线程技术_第3页
可视化程序设计.多线程技术_第4页
可视化程序设计.多线程技术_第5页
已阅读5页,还剩42页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

2024/4/29可视化程序设计主讲:周建伟1内容提要进程与线程的概念应用程序域在C#中使用多线程线程同步问题线程同步的基本方法进程与线程的基本概念2024/4/293进程正在执行的程序称为进程。与进程相关的信息包括:进程标示(进程ID)、文件名、执行的程序和数据,运行时间、在存储器中的位置、占用的内存容量等。线程将一个进程划分为若干个独立的执行流,每一个执行流均称为一个线程。(1)线程是CPU调度和分配的基本单位。(2)每个进程都有一个主线程。(3)除了主线程以外,还可以给一个进程分配若干个子线程,从而达到多个任务并行执行的目的。进程(超市)食品服装体育用品文具数据(商品)线程(收银台)CPU(收银员)多线程应用程序域AppDomain把进程的4G空间又做了划分把一个大屋子进一步隔成多个小屋子从这个意义上,AppDomain是“亚进程”两个应用程序域占用独立的数据空间,且不能互相访问地址不能相同,因为在同一个进程里面代码空间可以重叠,相当于一个程序启动多遍线程同一时刻只能在一个应用程序域中运行默认的AppDomain是整个进程空间食品AppDomain(承包区)进程(超市)服装AppDomain家电AppDomain线程仍直属进程,它们可以先后运行于不同的AppDomain(收款台直属超市收完买服装的再收买食品的)进程管理(Process类)2024/4/297Process类常用的属性和方法-----------------------获取进程实例-------------------GetProcessById方法(静态方法):通过进程Id创建新的Process组件,并将其与本地计算机上的进程资源关联。GetProcessById最多只有一个Process实例。GetProcesses方法(静态方法):获取本机所有进程GetProcessesByName方法(静态方法):获取本机上特定名称的进程-----------------------获取及设置优先级---------------BasePriority属性:获取进程优先级(只读)PriorityClass属性:设置或更改进程优先级进程管理(Process类)2024/4/298---------------------------进程ID及进程名-----------------------Id属性:获取关联进程的唯一标识符ProcessName属性:获取该进程的名称,不包括路径和扩展名-----------------------------进程其他信息-------------------------------MachineName属性:获取关联进程正在其上运行的计算机名称MainModule属性:获取关联进程的主模块Modules属性:获取由关联进程加载的模块TotalProcessorTime属性:获取进程的总的处理器时间StartTime属性:获取关联进程的启动时间WorkingSet64属性:为进程分配的物理内存量(字节数)进程管理(Process类)2024/4/299-----------------------进程启动-------------------------Start方法:启动进程资源并将其与Process组件关联StartInfo属性:获取或设置要传递给启动进程的文件名以及启动参数-----------------------进程终止-------------------------Kill方法:强制终止进程CloseMainWindow方法:关闭具有用户界面的进程Close方法:释放与此组件关联的所有资源HasExited属性:指示关联进程是否已终止WaitForExit方法:设置等待关联进程退出的时间,并在该段时间结束前或该进程退出前,阻止当前线程执行。获取进程信息2024/4/29101、如何获取进程信息(1)获取本地计算机的所有进程:

Process[]myProcesses=Process.GetProcesses();(2)获取本地计算机上指定名称的进程:

Process[]myProcesses=

Process.GetProcessesByName("进程名称");注意:(a)进程名称不带扩展名。(b)可以是任何一个可执行文件例如:

Process[]myProcesses=Process.GetProcessesByName("WindowApplication1");获取进程信息2024/4/2911(3)获取远程计算机的所有进程:Process[]myProcesses=Process.GetProcesses(remoteMachineName);例如:

Process[]myProcesses=

Process.GetProcesses("");(4)获取远程计算机上指定名称的进程:

Process[]myProcesses=Process.GetProcessesByName("远程进程名称",remoteMachineName);启动和停止进程2024/4/29121.启动进程:方法1:(1)创建一个Process组件的实例,例如:

ProcessmyProcess=newProcess();(2)设置其对应的StartInfo属性,指定要运行的应用程序名以及传递的参数:

myProcess.StartInfo.FileName="文件名";

process1.StartInfo.Arguments="参数";如果该进程带有图形用户界面,也可以指定图形用户界面的打开方式。例如:

myProcess.StartInfo.WindowStyle=ProcessWindowStyle.Normal;(3)调用该实例的Start方法启动该进程。方法2:

直接调用Process类提供的静态方法启动进程。启动和停止进程2024/4/29132.停止进程通过两种方法利用Process组件停止进程。(1)如果进程有图形用户界面,调用CloseMainWindow方法。(2)如果进程没有用户界面,调用进程的Kill方法。不论有没有图形用户界面,如果希望强行让其退出,在权限允许的情况下,均可以调用Kill方法终止该进程。线程管理(Thread类)2024/4/2914Thread类位于System.Threading命名空间下。Thread类是用于创建和控制线程的,对线程的常用操作有:启动线程、终止线程、合并线程和让线程休眠等。Thread类提供的常用属性IsAlive属性:获取一个值,该值指示当前线程的执行状态。如果此线程已启动并且尚未正常终止,则为true;否则为falseIsBackground属性:获取或设置一个值,该值指示某个线程是否为后台线程。是后台线程或即将成为后台线程,则为true;否则为falsePriority属性:获取或设置一个值,该值指示线程的调度优先级线程管理(Thread类)2024/4/2915Thread类提供的常用方法Start方法:启动线程Join方法:将指定的线程合并到当前线程中,并阻止当前线程执行,直到指定的线程终止或经过了指定的时间为止Sleep方法:将当前线程阻止指定的毫秒数,零(0)表示应挂起此线程以使其他等待线程能够执行Abort方法:在调用此方法的线程上引发ThreadAbortException,以开始终止此线程的过程。调用此方法通常会终止线程前台线程和后台线程2024/4/2916一个线程要么是后台线程要么是前台线程。后台线程与前台线程类似,区别是后台线程不会影响进程终止。属于某个进程的所有前台线程都终止后,公共语言运行库就会结束该进程,而且所有属于该进程的后台线程也都会立即停止,而不管后台工作是否完成。利用Thread对象的IsBackground属性,可以设置或判断一个线程是后台线程还是前台线程。通过将某个线程的IsBackground属性设置为true,使其变为后台线程。默认情况下,属于托管线程池的线程(即其IsThreadPoolThread属性为true的线程)都是后台线程,通过创建并启动新的Thread对象而生成的线程都是前台线程。线程的基本操作2024/4/29171.启动线程启动线程前,首先要创建一个线程。创建无参数线程的一般形式为:

Threadt1=newThread(线程名);创建带参数线程的一般形式为(传递一个Object类型的参数):

Threadt2=newThread(线程名(Objectobj));创建线程实例后,就可以调用Start方法启动线程了。例如:

t1.Start();//不带参数

t2.Start(“myClass”);//带参数注意:调用Start只是告诉系统启动该线程,但是系统并不一定会立即启动它。线程的基本操作2024/4/29182.终止线程

两种方法:事先设置一个布尔字段,在其他线程中通过修改该布尔量的值作为传递给该线程是否需要终止的判断条件,而在该线程中循环判断该布尔值,以确定是否退出线程,这是结束线程比较好的方法,实际应用中一般使用这种方法。调用Thread类的Abort方法,该方法的最终效果是强行终止线程。线程的基本操作2024/4/29193.暂停线程在多线程应用程序中,有时候并不希望某一个线程继续执行,而是希望该线程暂停一段时间,这样,CPU就会将其时间片中剩余的部分让给另一个线程。调用Thread类的Sleep方法可以实现这个功能。例如:

Thread.Sleep(1000);

这条语句的功能是让当前线程暂停1000毫秒。注意

Sleep方法是静态方法,暂停的是该语句所在的线程,而不是其他线程。线程的基本操作2024/4/29204.合并线程Join方法用于把指定的线程合并到当前线程中,从而使其变为一个单个的线程。如果一个线程t1在执行的过程中需要等待另一个线程t2结束后才能继续执行,可以在t1的代码块中调用t2的join方法。例如:

t2.Join();

功能:t1在执行到t2.Join()语句后,就处于暂停状态,直到t2结束后才会继续执行。为了避免t1一直等待,可以在调用t2的Join方法的时候指定一个暂停时间,例如:t2.Join(100);在一个线程中操作另一个线程的控件2024/4/2921默认情况下,在Windows应用程序中,.NETFramework不允许在一个线程中直接操作另一个线程中的控件,这是因为访问Windows窗体控件本质上不是线程安全的。在应用程序中,如果创建某控件的线程之外的其他线程试图调用该控件,则系统会引发一个InvalidOperationException异常。有两种办法可以解决这个问题:(1)使用委托(delegate)操作另一个线程中的控件(2)用BackgroundWorker组件在后台执行线程在一个线程中操作另一个线程的控件(续)2024/4/2922利用委托调用另一个线程控件(方法1):delegatevoidAppendStringDelegate(stringstr);privatevoidAppendString(stringstr){

if(richTextBox1.InvokeRequired){

AppendStringDelegated=AppendString;richTextBox1.Invoke(d,str);}

else{richTextBox1.Text+=str;}}2024/4/2923利用委托调用另一个线程控件(方法2):privatedelegatevoidAppendStringDelegate(string

str);publicvoidAppendString(stringstr){richTextBox1.BeginInvoke(

newAppendStringDelegate(AddMessageToRichTextBox),

newObject[]{message});}privatevoidAddMessageToRichTextBox(stringmessage){richTextBox1.AppendText(message);}线程的优先级2024/4/2924五个优先级,由高到低分别是:Highest、AboveNormal、

Normal(默认)、BelowNormal和Lowest可以使用下面的方法为其赋予较高的优先级:

Threadt=newThread(MethodName);t.priority=ThreadPriority.AboveNormal;通过设置线程的优先级可以改变线程的执行顺序,所设置的优先级仅仅适用于这些线程所属的进程。注意:当把某线程的优先级设置为Highest时,系统正在运行的其他线程都会终止,所以使用这个优先级别时要特别小心。线程同步2024/4/2925为什么要同步 如果不同步,线程访问共享资源时可能出错对象读取读取写入写入线程的数据访问线程体是代码,代码要访问数据两个线程同时要求访问同一个数据?DataSetds=CreateDataSet();//......dataGridView1.DataSource=ds;DataSetds=CreateDataSet();foreach(DataTabledtinds.Tables{//....}ds.Tables.Add(newDataTable())//....//.....线程同步确保进程内的一个资源不能同时被写同时写,结果不可预知可以同时读,因为数据并不改变写的时候也不能读,数据不完整策略:确保写操作只有一个线程可执行。一个线程写的时候,其它线程全阻止(不能读也不能写)写完后,允许其它线程访问。如果下一个访问的线程也是写,则又一次阻止除自己外的所有线程没人写,不限制。线程同步2024/4/2928解决方法:System.Threading命名空间提供了多个用于同步线程的类这些类包括Mutex、Monitor、Interlocked等。在实际应用中经常使用lock语句完成线程同步。 该语句简化了编程的复杂性,使程序看起来既清晰又简洁一种简单的同步办法classProgram{staticboolcanRead=true;staticboolcanWrite=true;staticvoidMain(string[]args){Threadread1=newThread(ReadThread);read1.Start();Threadread2=newThread(ReadThread);read2.Start();Threadwrite1=newThread(WriteThread);read1.Start();Threadwrite2=newThread(WriteThread);write2.Start();}

staticpublicvoidWriteThread(){

if(canWrite){

canWrite=false;

canRead=false;//写数据

}

canWrite=true;

canRead=true;}staticpublicvoidReadThread(){

if(canRead){

canWrite=false;//读数据

}

canWrite=true;}}问题:非原子操作if(canWrite){canWrite=false//...}moveax,canWritecmpeax,0jleexitmoveax,-1movcanWrite,eax....exit:retmoveax,canWritecmpeax,0jleexitmoveax,-1movcanWrite,eax....exit:ret将某些操作为原子操作仍是多行代码,看作一个原子操作Interlocked类AddCompareExchangeDecrementExchangeIncrementReadInterlocked.AddAdd(int,int)将一次整数加法作为原子操作进行结果放在第一个参数中不会出现加法做完一半的情况Interlocked.CompareExchangeCompareExchange(refintp1,intvalue,intp2);如果p1==p2,就p1=value,并返回原来p1的值同样,原子操作。改进的简单的同步办法classProgram{staticint

canRead=1;staticint

canWrite=1;staticvoidMain(string[]args){Threadread1=newThread(ReadThread);read1.Start();Threadread2=newThread(ReadThread);read2.Start();Threadwrite1=newThread(WriteThread);read1.Start();Threadwrite2=newThread(WriteThread);write2.Start();}

staticpublicvoidWriteThread(){

if(canWrite==1){

Interlocked.Add(canWrite,-1);Interlocked.Add(canRead,-1);

//canWrite=false;

canRead=false;//写数据}

Interlocked.Add(canWrite,-1);Interlocked.Add(canRead,-1);//

canWrite=true;

canRead=true;}仍存在的问题对canWrite和canRead的操作仍不是原子的canWrite==1的比较,也不是原子的

staticpublicvoidWriteThread(){

if(canWrite==1){

Interlocked.Add(canWrite,-1);Interlocked.Add(canRead,-1);

//canWrite=false;

canRead=false;//写数据}

Interlocked.Add(canWrite,-1);Interlocked.Add(canRead,-1);//

canWrite=true;

canRead=true;}lock语句2024/4/2936lock语句的功能

lock语句代码段(语句块)标记为临界区。它能确保当一个线程位于代码的临界区(可以理解为一段代码)时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码段,则它将一直等待(即被阻塞),直到锁定的对象被释放以后才能进入临界区。lock语句的用法首先利用lock语句锁定某一个对象,然后执行代码段中的语句,等代码段中的语句执行完毕后,再释放该对象。privateObjectobj=newObject();……lock(obj){//临界区中的代码}lock语句2024/4/2937使用lock语句应注意的问题:锁定的对象名(上面代码中的obj)一般声明为Object类型,不要将其声明为值类型。锁定的对象名不能将其声明为public,只能为private临界区中的代码一般不宜太多。线程池2024/4/2938背景:

1)无限制的创建线程消耗系统资源

2)创建线程、回收线程均需要时间线程池:是在后台执行多个任务的线程集合。

1)最大线程数限制。如果所有线程都繁忙,则额外的任务将放入等待队列中,直到有线程可用时才能够得到处理。

2)最小线程数=创建线程池时应立即启动的数目

3)一旦池中的某个线程完成任务,它将返回到等待线程队列中,等待被再次使用。这种重用使应用程序可以避免为每个任务创建新线程引起的资源和时间消耗。

4)一项工作任务被加入到线程池的队列中,就不能取消该任务,直到该任务完成。线程池2024/4/2939为什么要用线程池?降低系统开销可以重用资源,使应用程序可以避免为每个任务创建新线程引起的资源和时间消耗。什么情况下才使用线程池?后台执行,而且不同线程没有优先级区别适用于需要多个线程而实际执行时间又不多的场合没有导致线程长时间被阻塞的任务(对于可能长时间被阻塞的任务,应该创建单独的线程处理,不应该使用线程池),这是因为线程池具有最大线程数限制,大量阻塞的线程池线程可能会阻止任务启动线程池

2024/4/2940ThreadPool类位于System.Threading命名空间下。ThreadPool提供了对线程池的操作(静态方法),例如:发送工作项、处理异步I/O、设置线程数目等ThreadPool是一个静态类托管线程池中的线程为后台线程,即它们的IsBackground属性为true。这意味着在所有的前台线程都已退出后,ThreadPool线程也会自动退出线程池2024/4/2941GetAvailableThreads方法:检索由GetMaxThreads返回的线程池线程的最大数目和当前活动数目之间的差值GetMaxThreads方法:检索可以同时处于活动状态的线程池请求的数目。所有大于此数目的请求将保持排队状态,直到线程池线程变为可用GetMinThreads方法:检索线程池在新请求预测中维护的空闲线程数SetMaxThreads方法:可以同时处于活动状态的线程池的请求数目。所有大于此数目的请求将保持排队状态,直到线程池线程变为可用。SetMinThreads方法:设置线程池在新请求预测中维护的空闲线程数线程池2024/4/2942QueueUserWorkItem方法功能:功能:请求线程池处理一个任务或者工作项运行时线程池会自动为每一个任务创建线程并且在任务释放时释放线程。语法:带一个

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论