第9章 多线程 王德俊 上海交通大学继续教育学院.

Slides:



Advertisements
Similar presentations
软件编程基础 一、程序的编辑 Java 源程序是以 Java 为后缀的简单的文本文件,可以用各种 Java 集成开发环境中的源代码编辑器来编写,也可以用其他文 本编辑工具,如 Windows 中的记事本或 DOS 中的 EDIT 软件等。 利用文字编辑器编写下列程序 public class Hello.
Advertisements

多元評量與 Greenfoot 簡介 南港高中高慧君. 演講大綱 多元評量 高中階段程式設計教學目標與困境 Greenfoot 快速入門 – 袋熊吃樹葉 – 沙灘螃蟹 Greenfoot 臺灣社群介紹 2.
第三讲 面向对象(上).
3.2 Java的类 Java 类库的概念 语言规则——程序的书写规范 Java语言 类库——已有的有特定功能的Java程序模块
JAVA 编 程 技 术 主编 贾振华 2010年1月.
项目7 面向对象高级.
项目6 通用堆栈.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
四資二甲 第三週作業 物件導向程式設計.
C#程序设计案例教程 第3章 程 序 结 构.
面向对象的程序设计(一).
设计模式可以帮助我们改善系统的设计,增强 系统的健壮性、可扩展性,为以后铺平道路。
C#程序设计 10软件1、2班 王槐彬 计算机工程学院.
第二章 JAVA语言基础.
類別與物件 Class & Object.
Ch07 介面與多重繼承 物件導向程式設計(II).
第三章 控制结构.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
Ch08 巢狀類別 物件導向程式設計(II).
程式設計實作.
第5章 异常处理 王德俊 上海交通大学继续教育学院.
C#程序设计 c# programming 泛型 C#程序设计课程组.
.NET 程式設計入門(使用 C#) 講師:鄧智鴻.
第二章 C# 基础知识.
第四章 在 C# 中实现 OOP 概念.
程序與函數的類別方法 目的:模組化程式設計 方法:由上而下設計 注意事項:(1)獨立性 (2)結合問題 (3)子問題間的溝通.
C#程序设计 c# programming 多线程 C#程序设计课程组.
第14章 多线程和异步程序设计 14.1 多线程程序设计 14.2 异步程序设计.
程式設計實作.
C#程序设计基础 $3 成员、变量和常量.
2018/12/3 面向对象与多线程综合实验-网络编程 教师:段鹏飞.
Java程序设计 第9章 继承和多态.
第4章 数组和集合 4.1 一维数组 4.2 二维数组 4.3 Array类 4.4 交错数组 4.5 ArrayList类
第三章 流程控制與例外處理 資訊教育研究室 製作 注意:本投影片僅供上課使用,非經同意,請勿散播或轉載。
C#面向对象程序设计 $7 继承和多态性.
并发机制 结果应该为: 线程 1: 1 线程 1: 2 线程 1: 3 线程 1: 4 线程 1: 5 线程 2: 6 线程 2: 7
第一次课后作业 1. C/C++/Java 哪些值不是头等程序对象 2. C/C++/Java 哪些机制采用的是动态束定
9.1 程式偵錯 9.2 捕捉例外 9.3 自行拋出例外 9.4 自定例外類別 9.5 多執行緒
第9讲 Java的继承与多态(一) 类的继承 子类的创建 方法覆盖.
$16 进程和线程. $16 进程和线程 进程 进程 属性 ProcessName / Id MachineName / MainModule BasePriority StartTime / ExitTime TotalProcessorTime / UserProcessorTime PrivateMemorySize64.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
第5讲 使用类和对象编程(三) 内部类 实例 程序控制结构 选择语句.
Ch02-基礎語法.
C/C++/Java 哪些值不是头等程序对象
C#程序设计基础 第二章 数据类型.
第六章 属性、索引器、委托和事件.
類別與物件 I (Classes and Objects I)
* 單元:電腦與問題解決 主題:Java物件導向程式設計-類別與物件 台南縣國立善化高中 蕭嘉民 老師
第三章 C# 基础知识.
辅导课程八.
JAVA 编 程 技 术 主编 贾振华 2010年1月.
第二章 Java基本语法 讲师:复凡.
C#程序设计基础 $3 成员、变量和常量.
Java程式初體驗大綱 大綱 在學程式之前及本書常用名詞解釋 Hello Java!程式 在Dos下編譯、執行程式
第二章 Java语法基础.
第二章 Java基本语法 讲师:复凡.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
龍老師我不會Debug QQ.
#include <iostream.h>
第二章 Java基本语法 讲师:复凡.
第6單元 6-1 類別的繼承 (Class Inheritance) 6-2 抽象類別 (Abstract Class)
JAVA 程式設計與資料結構 第三章 物件的設計.
對於成員(member)存取權的限制 成員的資料被毫無限制的存取,任誰都可以指定任意值給成員,Java語言為了防止這種現象的產生,規定:有一種成員的資料不能任由類別外部的任何人隨意存取。
判斷(選擇性敘述) if if else else if 條件運算子.
第二章 Java基础语法 北京传智播客教育
輸出執行結果到螢幕上 如果要將執行結果的文字和數值都「輸出」到電腦螢幕時,程式要怎麼寫? class 類別名稱 {
第二章 Java基本语法 讲师:复凡.
第6章 继承和多态 伍孝金
Summary
Presentation transcript:

第9章 多线程 王德俊 上海交通大学继续教育学院

第9章 多线程 9.1 线程及其实现方法 9.2 线程的同步控制 2

9.2 线程的同步控制 9.2.1 为什么要同步控制 9.2.2 使用ManualResetEvent类 9.2.3 使用AutoResetEvent类

9.2.1 为什么要同步控制 线程之间由于共享资源而产生相互之间需要互相等待、互通消息才能正确地完成任务,此时就需要相应的同步控制机制来支持线程的合作关系。

9.2 线程的同步控制 9.2.1 为什么要同步控制 【例9.1】 存在同步访问问题的多线程程序。如下:创建控制台应用程序BankTransfering,简单地模拟银行用户进行转帐和取款的程序: class Bank { private double account1 = 2500; private double account2 = 1000; public void transfering() //转帐 Console.WriteLine("转帐前帐户account1还剩余的金额:" + account1.ToString()); Console.Write("转帐金额(元):"); double sum = double.Parse(Console.ReadLine()); //输入转帐金额 if (sum > account1) Console.WriteLine("转帐金额超出了帐户account1所剩的金额,"+"转帐失败!"); return; } account1 = account1 - sum; account2 = account2 + sum; Console.WriteLine("转帐后帐户account1还剩余的金额:" + account1.ToString());

9.2 线程的同步控制 9.2.1 为什么要同步控制 public void fetching() //取款 { Thread.Sleep(100); account1 = account1 - 2000; //取款2000元 } static void Main(string[] args) Bank a = new Bank(); Thread user1 = new Thread(new ThreadStart(a.transfering)); Thread user2 = new Thread(new ThreadStart(a.fetching)); user1.Start(); user2.Start(); Console.ReadKey();

其原因:恰好在user1等待接收从键盘输入的转帐金额时,user2从帐户account1上提走了2000元。 结论:需要同步控制 9.2 线程的同步控制 9.2.1 为什么要同步控制 程序运行结果如图: 分析:user1查询帐户account1时,显示了还剩2500元的信息,但在执行从account1向account2转2000元时,却出现了操作失败的提示。 其原因:恰好在user1等待接收从键盘输入的转帐金额时,user2从帐户account1上提走了2000元。 结论:需要同步控制

ManualResetEvent类的作用是:通知一个或多个正在等待的线程已发生事件。 9.2 线程的同步控制 9.2.2 使用ManualResetEvent类 ManualResetEvent类的作用是:通知一个或多个正在等待的线程已发生事件。 ManualResetEvent类对象有两种状态:有信号状态和无信号状态。状态常通过两种方法设置: 一种是使用构造函数, 例如: ManualResetEvent mre = new ManualResetEvent( false); //初始化mre为无信号状态 ManualResetEvent mre = new ManualResetEvent(true); //初始化mre为有信号状态 另一种是使用对象方法, 例如: mre.Reset(); //使mre处于无信号状态 mre.Set(); //使mre处于有信号状态 调用 Set 方法将使等待句柄一直保持终止状态,允许一个或多个等待线程继续,直到 Reset 方法被调用。

当ManualResetEvent类对象处于无信号状态时,调用该对象WaitOne()方法的线程将被阻止运行(暂停); 9.2 线程的同步控制 9.2.2 使用ManualResetEvent类 当ManualResetEvent类对象处于无信号状态时,调用该对象WaitOne()方法的线程将被阻止运行(暂停); 当该对象变为处于有信号状态时, WaitOne()方法收到信号, WaitOne()方法将解除该线程的暂停状态,使它继续运行。 实现多线程的同步控制方法是: 将被视为一体的语句序列置于Reset()和Set()方法之间(称为“加锁”) 需要与它们同步的线程,在读取共享变量前先调用WaitOne()方法;用于检测ManualResetEvent类对象有无信号: 无信号,则该线程被暂停 有信号,该线程才能继续执行,从而实现线程的同步控制。

9.2 线程的同步控制 9.2.2 使用ManualResetEvent类 1. 单线程的加锁 2. 多线程的加锁

1. 单线程的加锁 对程序BankTransfering出现问题的解决办法: 通过创建ManualResetEvent类的对象mre,并适当地添加mre.Reset() 和mre.Set() 添加同步控制 代码修改如下(红色部分): class Bank { private double account1 = 2500; private double account2 = 1000; //创建ManualResetEvent类的对象mre public ManualResetEvent mre = new ManualResetEvent(false);

public void transfering() //转帐 { mre.Reset(); //设置对象mre处于无信号状态 Console.WriteLine("转帐 前 帐户account1还剩余的金额: " + account1.ToString()); Console.Write("转帐金额(元):"); double sum = double.Parse(Console.ReadLine()); if (sum > account1) Console.WriteLine("转帐金额超出了帐户account1所 剩的金额," +"转帐失败!"); return; }

account1 = account1 - sum; Console.WriteLine("转帐 后 帐户account1还剩余的金额:" +account1.ToString()); mre.Set(); //设置对象mre处于有信号状态 } public void fetching() //取款 { //阻止当前线程(线程user2)的运行,直到收到对象mre发的信息 mre.WaitOne(); Thread.Sleep(100); account1 = account1 - 2000;

2. 多线程的加锁 问题: 一个ManualResetEvent类对象只能对一个线程中的语句序列进行加锁;

2. 多线程的加锁 【例9.2】 下列是控制台应用程序BankTransfering2中文件Program.cs的代码,它仍然模拟银行转帐、查账的功能,但对代码进行了简化(该程序需要解决多线程的同步控制问题): class Bank { private double account1 = 2500; private double account2 = 1000; public void transfering() //将100元从帐户account1转到帐户account2 account1 = account1 - 100; Thread.Sleep(100); account2 = account2 + 100; }

2. 多线程的加锁 public void transfering2() //将300元从帐户account2转到帐户 account1 { Thread.Sleep(200); account2 = account2 - 300; } public void querying() //查询帐户account1和account2上 的余额 Console.WriteLine("帐户account1上的余额为:{0} 元", account1); Console.WriteLine("帐户account2上的余额为:{0} 元", account2);

2. 多线程的加锁 static void Main(string[] args) { Bank a = new Bank(); Thread user1 = new Thread(new ThreadStart(a.transfering)); //转帐用户1 Thread user2 = new Thread(new ThreadStart(a.transfering2)); //转帐用户2 Thread user3 = new Thread(new ThreadStart(a.querying)); //查账用户 user1.Start(); //执行转帐(account1到account2) user2.Start(); //执行转帐(account2到account1) user3.Start(); //查账用户 Console.ReadKey(); }

2. 多线程的加锁 实现对两个线程中的语句进行加锁: (1)先创建一个包含两个ManualResetEvent类对象的ManualResetEvent数组mres: ManualResetEvent[] mres = { new ManualResetEvent(false), new ManualResetEvent(false) }; (2)用数组mres中的两个对象分别对方法transfering()和方法transfering2()中的代码进行加锁; (3)在方法querying()中查询语句之前调用WaitHandle.WaitAll()方法,该方法的参数类型是ManualResetEvent数组,其作用是:当数组中所有的对象都接收到信号后才允许方法querying()继续执行。

2. 多线程的加锁 修改后的代码: class Bank { private double account1 = 2500; ManualResetEvent[] mres = { new ManualResetEvent(false), new ManualResetEvent(false) }; //创建包含两个ManualResetEvent类对象的数组 public void transfering() //将100元从帐户account1转到帐户account2 mres[0].Reset(); account1 = account1 - 100; Thread.Sleep(100); account2 = account2 + 100; mres[0].Set(); }

2. 多线程的加锁 public void transfering2() //将300元从帐户account2转到帐户account1 { mres[1].Reset(); account1 = account1 + 300; Thread.Sleep(200); account2 = account2 - 300; mres[1].Set(); } public void querying() //查询帐户account1和account2上的余额 WaitHandle.WaitAll(mres); Console.WriteLine("帐户account1上的余额为:{0} 元", account1); Console.WriteLine("帐户account2上的余额为:{0} 元", account2);

分析: 9.2.3 使用AutoResetEvent类 9.2 线程的同步控制 9.2.3 使用AutoResetEvent类 分析: ManualResetEvent的缺点:当进行多线程的同步控制时,创建的ManualResetEvent类对象的数量要与线程的个数相同,这使程序代码显得比较累赘。 AutoResetEvent的特点:只需要创建一个AutoResetEvent对象,就可以完成对多个线程的同步控制。 AutoResetEvent的Set()方法发出“一条”信号,就 “消掉”一个WaitOne()方法; 如果还有其他WaitOne()方法在等待信号,那么AutoResetEvent对象会自动变为无信号状态(如果没有就不改变其状态),直到再次执行一个Set()方法才能“消掉”下一个WaitOne()方法。

【例9.3】 修改程序BankTransfering2(见例9.2),使用AutoResetEvent类实现对其所涉及线程的同步控制。 9.2 线程的同步控制 9.2.3 使用AutoResetEvent类 【例9.3】 修改程序BankTransfering2(见例9.2),使用AutoResetEvent类实现对其所涉及线程的同步控制。 class Bank { private double account1 = 2500; private double account2 = 1000; AutoResetEvent are = new AutoResetEvent(false); public void transfering() //将100元从帐户account1转到帐户account2 are.Reset(); account1 = account1 - 100; Thread.Sleep(100); account2 = account2 + 100; are.Set(); }

public void transfering2() //将300元从帐户account2转到帐户account1 { 9.2 线程的同步控制 9.2.3 使用AutoResetEvent类 public void transfering2() //将300元从帐户account2转到帐户account1 { are.Reset(); account1 = account1 + 300; Thread.Sleep(200); account2 = account2 - 300; are.Set(); } public void querying() //查询帐户account1和account2上的余额 are.WaitOne(); Console.WriteLine("帐户account1上的余额为:{0} 元", account1); Console.WriteLine("帐户account2上的余额为:{0} 元", account2); 注意:有多少个Set()方法就应该多少个WaitOne()方法与之对应,否则会出现无限等待或其他问题。

本讲小结 为什么要同步控制 使用ManualResetEvent类 使用AutoResetEvent类