c程序设计教程课件完整版.docx

上传人:b****5 文档编号:7963185 上传时间:2023-01-27 格式:DOCX 页数:33 大小:36.81KB
下载 相关 举报
c程序设计教程课件完整版.docx_第1页
第1页 / 共33页
c程序设计教程课件完整版.docx_第2页
第2页 / 共33页
c程序设计教程课件完整版.docx_第3页
第3页 / 共33页
c程序设计教程课件完整版.docx_第4页
第4页 / 共33页
c程序设计教程课件完整版.docx_第5页
第5页 / 共33页
点击查看更多>>
下载资源
资源描述

c程序设计教程课件完整版.docx

《c程序设计教程课件完整版.docx》由会员分享,可在线阅读,更多相关《c程序设计教程课件完整版.docx(33页珍藏版)》请在冰豆网上搜索。

c程序设计教程课件完整版.docx

c程序设计教程课件完整版

集团标准化办公室:

[VV986T-J682P28-JP266L8-68PNN]

 

c程序设计教程课件

本文由x4168138贡献

ppt文档可能在WAP端浏览体验不佳。

建议您优先选择TXT,或下载源文件到本机查看。

第9章多线程

本章内容

9.1一个简单的多线程应用程序9.2线程及其实现方法9.3线程的同步控制9.4线程池9.5线程对控件的访问

9.1一个简单的多线程应用程序C#程序设计教程——蒙祖强编着

本小节创建的多线程应用程序一共包含两个线程,本小节创建的多线程应用程序一共包含两个线程,这两个线程并发地在屏幕上输出相关的字符串。

发地在屏幕上输出相关的字符串。

程序的关键代码如下:

程序的关键代码如下:

classA{publicstaticintn=0;publicvoidf(){for(inti=0;i<10;i){Console.WriteLine("f()在输出:

{0}",A.n);在输出:

在输出A.n;让当前线程Thread.Sleep(100);睡眠100毫秒睡眠毫秒}}}

9.1一个简单的多线程应用程序C#程序设计教程——蒙祖强编着

classB{publicstaticvoidg(){for(inti=0;i<10;i){Console.WriteLine("g()在输出:

{0}",A.n);在输出:

在输出A.n;让当前线程Thread.Sleep(100);睡眠100毫秒睡眠毫秒}}}staticvoidMain(string[]args){Aa=newA();ThreadStartthst1=newThreadStart(a.f);建立委托对象,建立委托对象,使之创建线程thst1创建线程每个线程实际上是可见,与给定的方法相关联类可见,每个线程实际上是Thread类ThreadStartthst2=newThreadStart(B.g);的对象,它是通过Thread类的构造的对象,它是通过类的构造Threadth1=newThread(thst1);函数来创建;函数来创建;并且每个线程都与既Threadth2=newThread(thst2);定的方法相关联th1.Start();启动线程创建线程thst2创建线程th2.Start();Console.ReadKey();}

——由这个例子可以看到,Thread类、委托类型由这个例子可以看到,由这个例子可以看到类委托类型ThreadStart等——执行线程和th2实际上是执行方法执行线程th1和实际上是执行方法a.f()和方法和方法B.g()。

等。

执行线程实际上是执行方法和方法是多线程程序设计中的核心内容。

是多线程程序设计中的核心内容。

9.2线程及其实现方法

9.2.1线程的概念

C#程序设计教程——蒙祖强编着

线程的概念与程序、进程的概念密切相关。

线程的概念与程序、进程的概念密切相关。

程序是程序员编写的静态代码文本。

程序是程序员编写的静态代码文本。

是程序员编写的静态代码文本进程则是程序的一次动态执行过程,进程则是程序的一次动态执行过程,进程运行时需要占用装载则是程序的一次动态执行过程程序代码(编译后的可执行代码)程序代码(编译后的可执行代码)以及存放其所需数据的内存空间和其他的机器资源(如文件等),),当进程终止时这些内存空间间和其他的机器资源(如文件等),当进程终止时这些内存空间和资源也随之释放。

显然,同一个程序,和资源也随之释放。

显然,同一个程序,它可以被多次加载到不同的内存区域中、使用不同的机器资源,同的内存区域中、使用不同的机器资源,从而形成多个不同的进即一个程序可以形成多个进程。

程,即一个程序可以形成多个进程。

一个进程是由多个执行单元组成,每个执行单元就是一个线程,一个进程是由多个执行单元组成,每个执行单元就是一个线程,即进程是由多个线程组成。

即进程是由多个线程组成。

每个线程都共享着其进程所占用的内存空间和机器资源(如堆栈、),实际上存空间和机器资源(如堆栈、CPU、寄存器等),实际上,一个、寄存器等),实际上,线程是一组机器指令以及它共享的内存和资源是一组机器指令以及它共享的内存和资源。

线程是一组机器指令以及它共享的内存和资源。

9.2线程及其实现方法

9.2.1线程的概念线程和进程的主要区别在于:

线程和进程的主要区别在于:

C#程序设计教程——蒙祖强编着

进程是由多个线程组成,即线程是进程的一个组成部分。

进程是由多个线程组成,即线程是进程的一个组成部分。

线程的划分尺度小,具有较高的并发效率。

线程的划分尺度小,具有较高的并发效率。

进程独占相应的内存和资源(其他进程不能使用),线程则是进程独占相应的内存和资源(其他进程不能使用),线程则是),共享进程所拥有的内存和资源(其他线程也可以使用),),从而极共享进程所拥有的内存和资源(其他线程也可以使用),从而极大地提高运行效率。

大地提高运行效率。

进程提供多个线程执行控制,而每个线程只能有一个运行入口、进程提供多个线程执行控制,而每个线程只能有一个运行入口、顺序执行序列和出口(线序”执行)。

顺序执行序列和出口(“线序”执行)。

进程可以独立执行,但线程不能独立执行,进程可以独立执行,但线程不能独立执行,而必须依赖于进程所提供的环境。

所提供的环境。

9.2线程及其实现方法

9.2.2线程的实现方法

C#程序设计教程——蒙祖强编着

线程的创建和应用主要是由Thread类和线程的创建和应用主要是由类和ThreadStart委托来实现。

委托来实现。

类和委托来实现Thread类的构造函数和主要方法说明如下:

类的构造函数和主要方法说明如下:

类的构造函数和主要方法说明如下构造函数Thread类构造函数的作用是用于创建线程,它主要有两个重载版类构造函数的作用是用于创建线程,类构造函数的作用是用于创建线程本:

构造函数创建的线程关联没

有参数的方法

publicThread(ThreadStartstart)publicThread(ParameterizedThreadStartstart)——每个线程都必须关联一个无返回类型的方法(称为线程方每个线程都必须关联一个无返回类型的方法(每个线程都必须关联一个无返回类型的方法),如果关联的方法无参数类型或构造函数创建的线程关联带法),如果关联的方法无参数,则用第一个构造函数创建线程;其中,参数参数start是ThreadStart类型或类型或ParameterizedThreadStart其中,如果关联的方法无参数,则用第一个构造函数创建线程;是一个object类型参数的方法一个类型参数的方法如果关联的方法带一个参数,则用第二个构造函数创建线程。

如果关联的方法带一个参数,则用第二个构造函数创建线程。

类型的变量。

这两种委托类型的声明如下:

类型的变量。

这两种委托类型的声明如下:

——当然,线程间数据的传递也可以使用对象的成员变量或方法当然,当然publicdelegatevoidThreadStart()来实现,这也是常用的方法,但要在线程的同步控制下进行。

来实现,这也是常用的方法,但要在线程的同步控制下进行。

publicdelegatevoidParameterizedThreadStart(Objectobj)

9.2线程及其实现方法

9.2.2线程的实现方法

C#程序设计教程——蒙祖强编着

例如,下面代码先定义类A,它有两个静态方法f()和g(),其中后例如,下面代码先定义类,它有两个静态方法和,者带有object类型的参数:

类型的参数obj:

者带有类型的参数classA{publicstaticvoidf(){Console.WriteLine("这是关联方法的线程这是关联方法f()的线程这是关联方法的线程");}publicstaticvoidg(objectobj){Console.WriteLine("这是关联方法的线程:

这是关联方法g()的线程:

这是关联方法的线程"obj.ToString());}}

9.2线程及其实现方法

9.2.2线程的实现方法

C#程序设计教程——蒙祖强编着

然后用上述两种构造函数分别通过委托类型ThreadStart和然后用上述两种构造函数分别通过委托类型和ParameterizedThreadStart创建线程和th2,它们分别关联方法创建线程th1和,创建线程f()和g(),并(在Main()方法中)执行它们:

方法中)和,方法中执行它们:

ThreadStartthst=newThreadStart(A.f);ParameterizedThreadStartpthst=newParameterizedThreadStart(A.g);Threadth1=newThread(thst);//关联方法关联方法f()关联方法Threadth2=newThread(pthst);//关联方法,带一个参数关联方法g(),关联方法th1.Start();//启动线程(执行方法)启动线程th1(执行方法f())启动线程th2.Start(200);//启动线程(执行方法,并将启动线程th2(执行方法g(),并将200作为参数值传启动线程作为参数值传给该方法)给该方法)

执行后,将输出如下结果:

执行后,将输出如下结果:

需要注意的是,线程关联的方法必须与所使用的委托类型相一致,需要注意的是,线程关联的方法必须与所使用的委托类型相一致,这是关联方法f()的线程这是关联方法返回类型必须为void,且在创建委托对象时关联的方法必须是已返回类型必须为的线程,这是关联方法g()的线程的线程:

这是关联方法的线程:

200经确定了的。

这些方法通常是类的静态方法和对象的方法。

经确定了的。

这些方法通常是类的静态方法和对象的方法。

9.2线程及其实现方法

9.2.2线程的实现方法

C#程序设计教程——蒙祖强编着

Start()方法方法该方法的作用是用于启动已经创建的线程,线程将进入Running该方法的作用是用于启动已经创建的线程,线程将进入状态(线程刚创建完时是处于Unstarted状态)。

状态)。

状态(线程刚创建完时是处于状态例如,以下代码是在前面代码中已经出现过的调用语句:

例如,以下代码是在前面代码中已经出现过的调用语句:

th1.Start();th2.Start(200);Abort()方法方法该方法用于终止线程,使线程进入AbortRequested状态。

该方法用于终止线程,使线程进入状态。

状态例如,终止线程的语句是的语句是:

例如,终止线程th的语句是:

th.Abort();

9.2线程及其实现方法

9.2.2线程的实现方法

C#程序设计教程——蒙祖强编着

Suspend()方法和方法和Resume()方法方法方法和Suspend()方法用于挂起线程,使线程进入方法用于挂起线程,状态;方法用于挂起线程使线程进入SuspendRequested状态;状态Resume()方法则用于将被挂起的线程重新工作,使得它进入方法则用于将被挂起的线程重新工作,方法则用于将被挂起的线程重新工作Running状态。

状态。

状态Join()方法方法假设在线程th1中对线程执行下列语句:

中对线程th2执行下列语句假设在线程中对线程执行下列语句:

th2.Join();这表示,将阻止线程th1的执行,直到th2执行完为止(才继续执这表示,将阻止线程的执行,直到执行完为止(的执行执行完为止)。

如果写成下列的形式行th1)。

如果写成下列的形式,则表示阻止线程,直到)。

如果写成下列的形式,则表示阻止线程th1,直到500毫毫秒以后th1才运行才运行:

秒以后才运行:

th2.Join(500);

9.2线程及其实现方法

C#程序设计教程——蒙祖强编着

9.2.2线程的实现方法以下是Thread类的主要属性:

类的主要属性:

以下是类的主要属性CurrentCulture属性属性该属性用于获取或设置当前线程的区域性。

该属性用于获取或设置当前线程的区域性。

CurrentThread属性属性获取当前正在运行的线程。

获取当前正在运行的线程。

CurrentUICulture属性属性获取或设置资源管理器使用的当前区域性,获取或设置资源管理器使用的当前区域性,以便在运行时查找区域性特定的资源。

域性特定的资源。

IsAlive属性属性该属性返回指示当前线程执行状态的值。

该属性返回指示当前线程执行状态的值。

IsBackground属性属性该属性用于获取或设置指示当前线程是否为后台线程。

值为true该属性用于获取或设置指示当前线程是否为后台线程。

值为时表示为后台线程,这时该线程随着主进程的结束而结束,时表示为后台线程,这时该线程随着主进程的结束而结束,而不管该线程是否已经运行结束;值为false(默认值)时表示为前台管该线程是否已经运行结束;值为(默认值)线程,只有所有的前台线程运行结束后,主线程才能终止。

线程,只有所有的前台线程运行结束后,主线程才能终止。

9.2线程及其实现方法

9.2.2线程的实现方法ManagedThreadId属性属性获取当前托管线程的唯一标识符。

获取当前托管线程的唯一标识符。

Name属性属性获取或设置线程的名称。

获取或设置线程的名称。

C#程序设计教程——蒙祖强编着

ThreadState属性属性返回当前线程的状态。

线程的状态包括Running、StopRequested、返回当前线程的状态。

线程的状态包括、、SuspendRequested、Background、Unstarted、Stopped、、、、、WaitSleepJoin、Suspended、AbortRequested和Aborted。

、、和。

9.2线程及其实现方法

9.2.3线程的优先级

C#程序设计教程——蒙祖强编着

线程的优先级是用Thread类的类的Priority属性来设置,其值集是一属性来设置,线程的优先级是用类的属性来设置个枚举,个枚举,即{Lowest,BelowNormal,Normal,AboveNormal,Highest},它们的优先级别依次从低到高,Priority属性的默认设,它们的优先级别依次从低到高,属性的默认设置是ThreadPriority.Normal。

置是。

需要注意的是,需要注意的是,操作系统并不能够保证拥有高优先级的线程每次都能够获得比低优先级线程更高执行权限,都能够获得比低优先级线程更高执行权限,这跟操作系统的调度算法有关。

算法有关。

9.3线程的同步控制

9.3.1为什么要同步控制

C#程序设计教程——蒙祖强编着

【例9.1】存在同步访问问题的多线程程序。

(为什么要同步控制)】存在同步访问问题的多线程程序。

为什么要同步控制)创建控制台应用程序BankTransfering,它只是简单地模拟银行用创建控制台应用程序,户进行转帐和取款的程序:

户进行转帐和取款的程序:

classBank{privatedoubleaccount1=2500;privatedoubleaccount2=1000;publicvoidtransfering()//转帐转帐{Console.WriteLine("转帐前帐户account1还剩余的金额:

"转帐帐户还剩余的金额:

还剩余的金额account1.ToString());Console.Write("转帐金额(元):

转帐金额():

");转帐金额doublesum=double.Parse(Console.ReadLine());//输入转帐金额输入转帐金额if(sum>account1){Console.WriteLine("转帐金额超出了帐户转帐金额超出了帐户account1所剩的金额,"所剩的金额,转帐金额超出了帐户所剩的金额"转帐失败!

");转帐失败!

转帐失败return;}

9.3线程的同步控制

9.3.1为什么要同步控制

C#程序设计教程——蒙祖强编着

account1=account1-sum;account2=account2sum;Console.WriteLine("转帐后帐户还剩余的金额:

转帐帐户account1还剩余的金额:

"还剩余的金额account1.ToString());}publicvoidfetching()//取款取款{Thread.Sleep(100);account1=account1-2000;//取款取款2000元取款元}}staticvoidMain(string[]args){Banka=newBank();Threaduser1=newThread(newThreadStart(a.transfering));Threaduser2=newThread(newThreadStart(a.fetching));user1.Start();user2.Start();Console.ReadKey();}

9.3线程的同步控制

9.3.1为什么要同步控制

C#程序设计教程——蒙祖强编着

程序中的类Bank定义了两个方法:

fetching()和transfering(),它程序中的类定义了两个方法:

和,定义了两个方法们分别用于实现取款和转帐操作,们分别用于实现取款和转帐操作,并基于这两个方法分别创建了线程user1和user2。

程序运行时,user1和user2几乎是同时开始工线程和。

程序运行时,和几乎是同时开始工随后user1从键盘接收转帐金额,然后完成转帐操作;但作,随后user2的“动作”比较快,立刻就取出的动作”比较快,立刻就取出2000元。

程序运行结果如元下图所示。

下图所示。

可以看到,查询帐户account1时,明明显示了还剩元的信息,可以看到,user1查询帐户查询帐户时明明显示了还剩2500元的信息,元的信息但在执行从account1向account2转2000元时,却出现了操作失败的提示元时,但在执行从向转元时即使转帐操作成功了,结果显示的剩余金额也不对)。

其原因在于,)。

其原因在于(即使转帐操作成功了,结果显示的剩余金额也不对)。

其原因在于,恰好在user1等待接收从键盘输入的转帐金额时,user2从帐户等待接收从键盘输入的转帐金额时,从帐户account1上恰好在等待接收从键盘输入的转帐金额时从帐户上提走了2000元。

显然,我们不希望发生这种情况,这就需要线程的同步提走了元显然,我们不希望发生这种情况,控制来解决。

控制来解决。

9.3线程的同步控制

9.3.2使用使用ManualResetEvent类类

C#程序设计教程——蒙祖强编着

ManualResetEvent类的作用是:

通知一个或多个正在等待的线程已类的作用是:

类的作用是发生事件。

发生事件。

ManualResetEvent类对象有两种状态:

有信号状态和无信号状态。

类对象有两种状态:

有信号状态和无信号状态。

类对象有两种状态其状态常通过两种方法设置:

一种是使用构造函数,其状态常通过两种方法设置:

一种是使用构造函数,另一种是对象方法。

例如:

方法。

例如:

使用构造函数

ManualResetEventmre=newManualResetEvent(false);ManualResetEventmre=newManualResetEvent(true);//初始化初始化mre为无信号状态初始化为无信号状态//初始化初始化mre为有信号状态初始化为有信号状态

mre.Reset();mre.Set();

//使mre处于无信号状态使处于无信号状态//使mre处于有信号状态使处于有信号状态

使用对象方法

9.3线程的同步控制

9.3.2使用使用ManualResetEvent类类

C#程序设计教程——蒙祖强编着

当ManualResetEvent类对象处于无信号状态时,调用该对象类对象处于无信号状态时,类对象处于无信号状态时WaitOne()方法的线程将被阻止运行(暂停);方法的线程将被阻止运行方法的线程将被阻止运行(暂停);当该对象变为处于有信号状态(方法收到信号)当该对象变为处于有信号状态(WaitOne()方法收到信号)时,方法收到信号WaitOne()方法将解除该线程的暂停状态,使它继续运行。

方法将解除该线程的暂停状态,方法将解除该线程的暂停状态使它继续运行。

据此,我们就可以实现多线程的同步控制。

方法是:

将被视为据此,我们就可以实现多线程的同步控制。

方法是:

一体的语句序列置于Reset()和Set()方法之间(称为“加锁”),方法之间(一体的语句序列置于和方法之间称为“加锁”与它们并发的线程,在读取共享变量前先调用WaitOne()方法;方法;与它们并发的线程,在读取共享变量前先调用方法这样在执行这些语句序列时由于ManualResetEvent类对象无信这样在执行这些语句序列时由于类对象无信因此该线程被暂停,直到它们执行完了以后才有信号,号,因此该线程被暂停,直到它们执行完了以后才有信号,该线程才能继续执行,因而避免读取不正确的数据,线程才能继续执行,因而避免读取不正确的数据,从而实现线程的同步控制。

程的同步控制。

9.3线程的同步控制

9.3.2使用使用ManualResetEvent类类

C#程序设计教程——蒙祖强编着

下面分两种情况来介绍如何对多线程进行同步控制。

下面分两种情况来介绍如何对多线程进行同步控制。

1.单线程的加锁对于上节介绍的程序BankTransfering,为解决其同步问题,可将对于上节介绍的程序,为解决其同步问题,代码修改如下(红色部分):

代码修改如下(红色部分):

classBank{privatedoubleaccount1=2500;privatedoubleaccount2=1000;//创建//创建ManualResetEvent类的对象mre创建ManualResetEvent类的对象类的对象mrep

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 农林牧渔 > 林学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1