C-程序设计-现代方法chp10-多线程.pptx

上传人(卖家):三亚风情 文档编号:3376570 上传时间:2022-08-25 格式:PPTX 页数:30 大小:427.30KB
下载 相关 举报
C-程序设计-现代方法chp10-多线程.pptx_第1页
第1页 / 共30页
C-程序设计-现代方法chp10-多线程.pptx_第2页
第2页 / 共30页
C-程序设计-现代方法chp10-多线程.pptx_第3页
第3页 / 共30页
C-程序设计-现代方法chp10-多线程.pptx_第4页
第4页 / 共30页
C-程序设计-现代方法chp10-多线程.pptx_第5页
第5页 / 共30页
点击查看更多>>
资源描述

1、第十章 多线程第十章 多线程210.1 案例-顺序执行的局限10.1.1 案例的设计与实现 案例在程序做其它的事情的同时,在屏幕上显示一个实时时钟。设计和实现可以将“其它的事情”编码为一个函数,显示实时时钟编码为另一个函数,然后然而,如果按照常规的编码模式,那么就不可能有然后了,因为常规模式并不支持同时做多件事。第十章 多线程310.1 案例-类型带来的困扰10.1.1 案例的设计与实现size_t counter=0;template void count(T&v)+counter;/v未使用,可能导致编译警告 int main()linked_list l1,2,3,4,5,6;l.tra

2、verse(count);std:cout the list has counter elements std:endl;return 0;将实际的函数作为回调传递个traverse。使用回调函数有哪些问题呢?可能会因需编写太多的回调函数。程序员对代码的控制较弱。此例还用到了全局变量。第十章 多线程410.1 案例-类型带来的困扰10.1.2 案例问题分析同步 Synchronizationf()g();g()return;停 等第十章 多线程510.2 关键概念10.2.1 异步异步 Asynchronizationf()g();g()return;第十章 多线程610.2 关键概念10.2

3、.2 进程和线程进程(process)是正在运行的程序的一个实例。每一个进程都有它自己的地址空间,以及与运行相关的各种系统资源。多个进程可以并发执行。一个进程是可调度的,调度的工作由系统来完成。被调度的进程可以处于三种状态之一:运行态(running)、就绪态(ready)和阻塞态(blocked)。处于运行态的进程占据CPU资源。处于阻塞态的进程不占据CPU资源。第十章 多线程710.2 关键概念10.2.2 进程和线程线程(thread)是进程中的实体,是被系统单独调度的基本单位。除了运行时必备的资源外,线程一般不占有系统资源,但所有线程共享所属进程的资源。一个进程至少有一个线程。线程可以

4、由另一个线程创建,并可以所属进程中的其它线程并发执行。与进程一样,线程也有同样的三种状态:运行态、就绪态和阻塞态。第十章 多线程810.2 关键概念10.2.3 互斥和共享read dthread A:+dwrite dread dthread B:-dwrite dtime一种顺序读写操作read dthread A:+dwrite dread dthread B:-dwrite dtime另一种顺序的读写操作d=0d=-1d=0d=1数据竞争 Data race数据竞争必然带来不确定性问题。同一时间只能有一个线程独占共享数据访问权的排他性操作称为互斥(mutual exclusion)。第

5、十章 多线程910.2 关键概念10.2.4 锁和死锁要实现互斥,需要利用锁(lock)机制。常见的锁分为排他锁(exclusive lock)和共享锁(shared lock)。排他锁用于互斥操作。如果线程A对资源d成功地加上了排他锁,那么称A拥有这把锁。此种情况下,系统会拒绝其它所有加锁申请,并阻塞申请者,直到A解锁(unlock)为止。此后,被阻塞的线程(中的一个或一些)将得到调度。共享锁用于共享操作。如果线程A对资源d成功地加上了共享锁,那么其它所有的加共享锁的申请都会被接受;但加排他锁的申请将会被拒绝,直到A解锁为止。第十章 多线程1010.2 关键概念10.2.4 锁和死锁 通过仔

6、细安排程序的逻辑,同时利用锁机制,可以确保线程的竞争是可控制的,从而避免了数据的混乱。但是,如果安排不当,或者发生了不可控的意外,那么可能导致更加严重的问题:死锁(dead lock)。例如,线程A对d加上了排他锁,但在解锁之前A被杀死(kill)了,那么d上的锁将永久存在,其他的申请者将永远处于阻塞态得不到调度。这显然是不合理的。由于程序的逻辑可能很复杂,因此预防死锁的发生代价较高。常见的处理方法是允许死锁的发生,但系统会介入处理。例如:如果系统发现了死锁,并在预定时间内没有解锁,那么系统会强制解锁。第十章 多线程1110.3 C+的多线程库10.3.1 头文件 10.3.1.1 threa

7、d类 thread类是多线程程序设计的基础,最常用的是它的一个构造函数:template explicit thread(F&f,Args&.args);这是线程类的一个成员模板。其中,参数f是线程的执行样板,可以是任意满足条件的可调用对象,最常见的情况是一个函数;Args是可调用对象的模板参数包;args是传递给该可调用对象的参数。第十章 多线程1210.3 C+的多线程库10.3.1 头文件 10.3.1.1 thread类 thread类模板的实例化将会创建一个线程对象(thread object)。例如:void f()/线程的执行样板std:thread t(f);上述定义创建了一个

8、名为t的线程对象,它唯一代表了一个新的执行线程(thread of execution),该执行线程的执行路线以函数f为样板。第十章 多线程1310.3 C+的多线程库10.3.1 头文件 10.3.1.1 thread类 被线程对象代表的执行线程具有可结合(joinable)属性。一般情况下,新创建的线程要结合(join)到创建者线程中。这意味着,后者要等待前者的完成。这实际上是两个线程的同步。如果一个执行线程没有被任何线程对象代表,那么这个执行线程是分离的(detached)。用thread类模板的默认构造函数创建的新线程就是这样的。另外,新线程可以主动与创建者线程分离(detach)。第

9、十章 多线程1410.3 C+的多线程库10.3.1 头文件 10.3.1.1 thread类void f()/*做一件相对耗时的事情*/int main()std:thread t(f);return 0;这段代码将导致异常的发生。原因是:主线程(main)先于子线程t结束,而f具有joinable属性。解决办法是在创建线程t后,调用:t.join();这意味着主线程main要等待t执行完。另个一个办法是在创建线程t后,调用:t.detach();这意味子线程t可能没执行完就被主线程杀死。第十章 多线程1510.3 C+的多线程库10.3.1 头文件10.3.1.2 名字空间this_thr

10、ead yield()此函数的功能是为其它线程提供被调度的机会。如果一个线程可能会占据CPU资源太久,那么可以在其中调用此函数,给其它线程执行的机会。sleep_until(abs_time)调用此函数模板的线程会被阻塞,直到超时。超时时限是个绝对时间,由参数abs_time指定。sleep_for(rel_time)调用此函数模板的线程会被阻塞,直到超时。超时时限是个相对时间,由参数rel_time提供。第十章 多线程1610.3 C+的多线程库10.3.2 头文件 10.3.2.1 mutex类 有用的成员:lock()由线程发起,申请进行加互斥锁操作。一旦申请线程加锁成功,那么它将成为这

11、个锁的拥有者;否则,申请线程会被阻塞。在拥有者解锁之前,其它申请用同一个mutex对象加互斥锁的线程都会被阻塞。解锁后,被阻塞的线程会得到调度的机会。unlock()锁的拥有者发起此解锁操作。此后,原线程不再是该锁的拥有者。原则上讲,锁的拥有者必须在适当时候解锁,否则会造成死锁。mutex m;void print(int i)m.lock();cout i ;m.unlock();mutex m;void print(int i)lock_guard lock(m);cout i ;/lock_guard的析构函数自动解锁第十章 多线程1710.3 C+的多线程库10.3.3 头文件 一个线

12、程可能把自己阻塞,并等待一个事件的发生;事件发生后,该线程恢复执行。完成这种功能需要定义在头文件中的(多种)类实例对象的参与。头文件中,最重要的类是condition_variable。第十章 多线程1810.3 C+的多线程库10.3.3 头文件1.void wait(unique_lock&lock)设有condition_variable类的示例cv,当线程t调用cv.wait(lock)时:wait自动调用lock.unlock(),并将线程阻塞在cv上。如果线程处于非阻塞态,则调用lock.lock(),然后返回。如果有解除阻塞信号到来,则wait将解除阻塞。简单地说,就是如果没有接

13、收到解除阻塞信号,那么wait将处于阻塞状态,不会返回;线程也因此处于等待状态。2.void notify_one()将阻塞线程中的某一个解除阻塞。3.void notify_all()将所有阻塞线程解除阻塞。第十章 多线程1910.3 C+的多线程库10.3.3 头文件condition_variable cv;template void g()thread_local unique_lock lk(m);print(thread,id,is waiting.);cv.wait(lk);print(thread,id,terminated);thread t1(g),t2(g),t3(g);

14、t1.detach();t2.detach();t3.detach();while(true)cin i;if(i=1)cv.notify_one();else if(i=2)cv.notify_all();else break;三个线程与主线程分离,后面的while语句才有执行的机会。线程被阻塞线程被调度please press a number to terminate thread(s):1 for 1,2 for all,3 for exit thread 2 is waiting.thread 3 is waiting.thread 1 is waiting.1thread 2 te

15、rminated2thread 3 terminatedthread 1 terminated3bye!第十章 多线程2010.3 C+的多线程库10.3.4 头文件 线程的执行往往是异步的。因此,如果一个线程依赖于另一个线程的结果,那么这两个线程需要同步。这样的同步操作可以使用定义在头文件中的类模板future和promise来实现。第十章 多线程2110.3 C+的多线程库10.3.4 头文件 术语解释1.两个异步执行的线程使用共享态(shared state)来进行结果通信。共享态包含了一些状态信息和(也许现在还没有产生的)结果。2.这些结果被封装在一个异步返回对象(asynchrono

16、us return object)中。3.一个异步返回对象的等待函数(waiting function)处于潜在的阻塞状态,等待共享状态已准备好(ready)。4.一个异步供应者(asynchronous provider)对象提供共享状态中的结果。一旦异步供应者准备好,那么等待函数将立即获得结果。第十章 多线程2210.3 C+的多线程库10.3.4 头文件 10.3.4.1 future和async future类模板定义了一个异步返回对象。它的模板参数是结果的类型,其主要成员是get(),用于从共享态中读取结果。调用get()的线程将处于潜在的阻塞状态,等待共享态准备好。future类模

17、板一般和异步函数模板async一起使用。async用于以异步方式(在一个潜在的新线程中)启动可调用对象,并提供一个封装在future对象里的、可调用对象产生的结果。/线程样板是一个lambdafuture result_f=async(launch:async,()-data this_thread:sleep_for(milliseconds(1000);return 1,A;);/get导致调用它的线程被阻塞data result=result_f.get();第十章 多线程2310.3 C+的多线程库10.3.4 头文件 10.3.4.2 promise类模板 Promise是一种异步程

18、序设计规范,多种程序设计语言实现了对它的支持。promise是一个异步供应者。它的模板参数是结果的类型,其成员set_value()为线程提供可获取的结果;另一个成员get_future()为线程提供了获得future对象的接口,这个future对象的类型参数与promise的一样。此后,线程通过该future对象获取promise提供的结果。promise pro;auto result_f=pro.get_future();thread t(promise&p)this_thread:sleep_for(milliseconds(1500);p.set_value(3,C);,ref(pr

19、o);/lambda不使用捕获,而是通过参数传递包围块中的对象t.detach();/get导致调用它的线程被阻塞auto result=result_f.get();第十章 多线程2410.3 C+的多线程库10.3.4 头文件 10.3.4.3 packaged_task 类模板packaged_task将可调用对象包装在一个任务(task)(是一种异步供应者)中,并且可以在这个任务启动时就设置任务将来返回的结果。线程通过调用这个模板的成员get_future()以便获取任务的结果。packaged_task tsk(task);auto result_f=tsk.get_future()

20、;/call of move()is very important!thread t(move(tsk),4,D);t.detach();auto result=result_f.get();第十章 多线程2510.3 C+的多线程库10.4 多线程编程示例设有这样一个很小的餐厅:有两位主厨(chef)、两位服务员(server)。当顾客点餐后:两位主厨开始做菜;做好一道菜后,一位服务员负责上菜。所有菜做完后,餐厅关门。现在,不考虑顾客的参与,编写一个程序来模拟这个餐厅的主厨和服务员的工作情况。第十章 多线程2610.3 C+的多线程库10.4 多线程编程示例10.4.1 系统简要分析从案例描

21、述可以得出,这是一种典型的生产者-消费者模型的应用。在这种模型中,生产者负责产生数据,消费者来消费数据;二者的运作是并发的,要竞争数据的访问权。因此需要对二者进行互斥和共享操作。2位厨师是生产者 2位服务员是消费者 竞争的数据是订单和做好的菜第十章 多线程2710.3 C+的多线程库10.4 多线程编程示例10.4.1 系统简要分析餐厅的活动图餐厅的用例图第十章 多线程2810.3 C+的多线程库10.4 多线程编程示例10.4.2 系统设计对象设计 将主厨和服务员设计为两个类。订单是一个list。这个list被初始化为顾客的所有订单。做好的菜也是一个list。开始的时候,这个list为空。主

22、厨每做好一道菜,就将其添加到这个list中。第十章 多线程2910.3 C+的多线程库10.4 多线程编程示例10.4.2 系统设计流程设计 考虑到同时工作的实际情况,程序流程应该是并发的。也就是说,需要设计主厨工作和服务员工作这两类线程。为了线程在执行时能获得更多的信息,为主厨和服务员类重载()运算符,是它们成为可调用对象,然后用这些类的对象充当线程模板。第十章 多线程3010.3 C+的多线程库10.4 多线程编程示例10.4.2 系统设计数据竞争处理 程序并发意味着数据竞争。这里,订单和做好的菜是两个主要的被竞争的数据。对于主厨们,需要一个互斥量来保证同一时间只有一位主厨读取订单;需要另一个互斥量来保证同一时间只有一位主厨添加做好的菜。此外,每添加一道菜,需要向服务员们发出信号。对于服务员们,需要一个条件变量来等待菜做好的信号;此外,需要一个互斥量来保证同一时间只有一位服务员上菜(并从做好的菜列表中移除)。另外,程序会用到标准输出流向屏幕输出信息。因此,需要一个互斥量来保证同一时间只有一个线程能够占据标准输出流。

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

当前位置:首页 > 办公、行业 > 各类PPT课件(模板)
版权提示 | 免责声明

1,本文(C-程序设计-现代方法chp10-多线程.pptx)为本站会员(三亚风情)主动上传,163文库仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。
2,用户下载本文档,所消耗的文币(积分)将全额增加到上传者的账号。
3, 若此文所含内容侵犯了您的版权或隐私,请立即通知163文库(发送邮件至3464097650@qq.com或直接QQ联系客服),我们立即给予删除!


侵权处理QQ:3464097650--上传资料QQ:3464097650

【声明】本站为“文档C2C交易模式”,即用户上传的文档直接卖给(下载)用户,本站只是网络空间服务平台,本站所有原创文档下载所得归上传人所有,如您发现上传作品侵犯了您的版权,请立刻联系我们并提供证据,我们将在3个工作日内予以改正。


163文库-Www.163Wenku.Com |网站地图|