多线程编程.pdf

上传人:qwe****56 文档编号:70023471 上传时间:2023-01-14 格式:PDF 页数:48 大小:269.24KB
返回 下载 相关 举报
多线程编程.pdf_第1页
第1页 / 共48页
多线程编程.pdf_第2页
第2页 / 共48页
点击查看更多>>
资源描述

《多线程编程.pdf》由会员分享,可在线阅读,更多相关《多线程编程.pdf(48页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。

1、多线程编程2007-09-062 0 0 5BAIDU CONFIDENTIAL内容提要 多线程编程简介 POSIX多线程使用 Linux线程实现 多线程编程与调试2 0 0 5BAIDU CONFIDENTIAL什么是多线程?一个进程内部的多个可并发执行的任务实体。多线程之间共享数据 抽象概念2 0 0 5BAIDU CONFIDENTIAL为什么使用多线程?提高系统的并发性 多cpu:多个线程在不同cpu上并发执行 单cpu:IO和cpu计算等的并行 提高程序的响应效率,特别是GUI程序 软件功能划分清晰2 0 0 5BAIDU CONFIDENTIAL多线程而不是多进程?线程相对与进程的

2、优点 性能好。线程启动、切换的开销都小于进程,内存占用也小于进程 共享数据。线程间数据直接可见,进程间需要使用IPC机制 方便移植。遵循POSIX标准2 0 0 5BAIDU CONFIDENTIALPOSIX线程 Portable Operating System Interface 遵循IEEE POSIX 1003.1c-1995标准 定义了线程API和行为 业界支持最广泛 多种实现:LinuxThreads、NGPT、NPTL 多种OS:Linux、Solaris、IRIX、AIX but Win32&OS/22 0 0 5BAIDU CONFIDENTIALPOSIX线程函数 POS

3、IX线程函数提供以下几类功能:线程的创建、终止、取消 线程属性控制 线程同步控制 线程专有数据的维护2 0 0 5BAIDU CONFIDENTIAL线程创建 int pthread_create(pthread_t*thread,pthread_attr_t*attr,void*(*start_routine)(void*),void*arg);启动一个新线程,线程执行体为start_routine函数 调用之后马上返回。创建成功返回0,失败则为非02 0 0 5BAIDU CONFIDENTIAL线程终止 线程执行体函数退出,线程就终止了 通过调用下面的函数退出:void pthread_

4、exit(void*retval);2 0 0 5BAIDU CONFIDENTIAL线程分离状态 非分离状态(缺省)当该状态的线程正常执行退出时,并不释放自身占用资源(线程描述体、线程栈)需要其他线程执行如下函数来join该线程:int pthread_join(pthread_t th,void*thread_return);调用pthread_join()的线程阻塞直到待join的线程退出,并释放退出线程占用的资源。2 0 0 5BAIDU CONFIDENTIAL线程分离状态(续)分离状态线程执行退出,自动释放其占用的资源。可能的问题:线程在pthread_create()调用返回之前

5、就退出,可能会导致返回错误的线程号。解决方法:线程开始时睡眠一小段时间2 0 0 5BAIDU CONFIDENTIAL简单代码实例void*run(void*arg)for(;)sleep(1);int main(int argc,char*argv)pthread_t tid;pthread_create(&tid,NULL,run,NULL);pthread_join(tid,NULL);return 0;2 0 0 5BAIDU CONFIDENTIAL常见问题:exit()出core 原因 按标准C的定义,exit函数需要把STDIO打开的句柄写buffer里的数据写下去,并释放打开

6、的句柄 一个进程调用exit两次,其行为是未定义的。我们可以认为exit不是一个线程安全的函数 解决方法 协调所有线程友好的退出 把exit()替换为向本进程发SIGKILL2 0 0 5BAIDU CONFIDENTIAL线程同步 线程同步机制:mutex,互斥锁 condition variable,条件变量 semaphore,信号量2 0 0 5BAIDU CONFIDENTIAL锁(mutex)与临界区 共享变量操作需要临界区,使多个线程对共享变量的访问串行化 不是对所有的共享变量操作都需要临界区,那些有竞争条件(race condition)的共享变量操作才需要临界区 锁是一种进程

7、(线程)间同步的协议,锁这个概念(mutual exclusion)很好解决了临界区问题2 0 0 5BAIDU CONFIDENTIALmutex加锁和解锁 加锁:pthread_mutex_lock(&mutex_var);当前mutex_var没有被其他线程加锁,则对其加锁,否则阻塞直到其他线程解锁,并再次尝试加锁 解锁:pthread_mutex_unlock(&mutex_var);2 0 0 5BAIDU CONFIDENTIAL线程锁的开销 锁操作开销比算数运算(指各种整数及浮点运算)慢得多,但比磁盘和网络I/O快得多 开销源于两个方面内核和线程库对锁的管理操作(如排队等)锁操作

8、引起的频繁进程/线程切换2 0 0 5BAIDU CONFIDENTIAL线程锁的粒度 纵向:被锁的区域(临界区)有多少操作,执行一次要多少时间。它描述的是多大规模的操作被串行。横向:就是一个锁锁住了多少资源。它描述的是有多少资源被串行访问。锁的粒度越小越有利于提高程序的并发度,但锁操作本身的开销越大。2 0 0 5BAIDU CONFIDENTIAL原子操作 原子操作是硬件提供的,不可中断指令单处理器下,单指令操作是原子操作多处理架构(SMP或CMP)下,多条指令可以同时执行,单指令本身不能保证原子操作 原子操作与线程锁都是解决临界区问题的方式,不同在于锁是软件级的,原子操作是硬件级的。2

9、0 0 5BAIDU CONFIDENTIAL常见问题:赋值是否需要加锁 共享变量简单赋值操作的并发问题 有一个共享变量status,有一个线程会修改status(修改为简单赋值,无中间值),其它线程只读不改。共享变量status必须是可以原子读和原子写 少于等于一个字长,正常的字节对齐 无竞争条件,因而不需要临界区,无需加锁2 0 0 5BAIDU CONFIDENTIAL线程安全的函数 在ANSI C开发时没有考虑到线程,因此C标准库函数有一些不是线程安全的:内部使用静态变量并作为返回值的函数,例如gmtime在一系列调用之间要求静态环境的函数,例如strtok pthreads定义了现存

10、函数的变体,相应函数名结尾加后缀_r,这些变体是线程安全的2 0 0 5BAIDU CONFIDENTIAL条件变量 pthread_cond_*系列函数 线程可以在条件变量上被阻塞和唤醒。无计数(与信号量不同),阻塞和唤醒需要显式进行,条件判断由用户自定义。必须和mutex结合使用,保证判断和阻塞的原子性。2 0 0 5BAIDU CONFIDENTIAL条件变量的创建和销毁 条件变量类型:pthread_cond_t 初始化方法有两种:pthread_cond_init(&cont_var,NULL);cond_var=PTHREAD_COND_INITIALIZER;销毁:pthread

11、_cond_destroy(&cont_var);2 0 0 5BAIDU CONFIDENTIAL阻塞和唤醒 阻塞于条件变量 pthread_cond_wait();pthread_cond_timedwait();/带超时 唤醒条件变量上的阻塞线程 pthread_cond_signal();pthread_cond_broadcast();/唤醒所有2 0 0 5BAIDU CONFIDENTIAL阻塞于条件变量 实例代码pthread_mutex_lock(&mutex_var);while(count=0)pthread_cond_wait(&cond_var,&mutex_var)

12、;count=count-1;pthread_mutex_unlock(&mutex_var);2 0 0 5BAIDU CONFIDENTIAL唤醒条件变量上的阻塞线程 实例代码pthread_mutex_lock(&mutex_var);if(count=0)pthread_cond_signal(&cond_var);count=count+1;pthread_mutex_unlock(&mutex_var);2 0 0 5BAIDU CONFIDENTIAL信号量 典型问题:生产者&消费者 线程间公共资源的访问计数器,本身带有计数信息 计数大于0时可正常访问,等于0时阻塞线程,等待其他

13、线程唤醒。其功能可以通过mutex和条件变量来实现2 0 0 5BAIDU CONFIDENTIAL代码实例void thread2()sem_wait(&sem_var);size-;do_thing2();void thread1()do_thing1();sem_post(&sem_var);size+;2 0 0 5BAIDU CONFIDENTIAL信号量还是线程锁条件变量 它们是两套原语,各有各的优点,都可以用,但是建议不要混用 最初信号量是被作为进程同步原语提出来的,而锁和条件变量是被作为线程同步原语提出来的 现在两者之间的界线不再明显,信号量也可以做线程同步,锁和条件变量也可以

14、做进程同步 POSIX信号量与pthreads函数风格很不相同 POSIX信号量函数在失败的情况下返回值是-1,错误号写errno POSIX信号量函数在信号处理(signal)之后不会自动重启,所以需要检查errno为EINTR的时候重启2 0 0 5BAIDU CONFIDENTIAL线程专有数据(TSD)线程级的全局变量 它看起来是全局的,但是为每个线程提供了一个复本 每个线程修改它,只有本线程看得见,其它线程感觉不到任何变化 现有 errno ul_log的log_level2 0 0 5BAIDU CONFIDENTIALTSD相关函数 int pthread_key_create(

15、pthread_key_t*key,void(*destr_function)(void*);/创建TSD键,并指定析构函数 int pthread_key_delete(pthread_key_t key);/释放TSD键 int pthread_setspecific(pthread_key_t key,const void*pointer);/指定TSD键关联的数据指针 void*pthread_getspecific(pthread_key_t key);/返回TSD键关联的数据指针2 0 0 5BAIDU CONFIDENTIAL线程与信号处理 不推荐在多线程程序中使用信号处理。特别

16、注意LinuxThreads实现与pthreads标准不兼容 标准要求外部信号要发送给进程的所有线程,但LinuxThreads实现只发送给一个线程 在signal_handler里要尽量少做操作,除非你有足够的把握,否则不要调用各种库函数2 0 0 5BAIDU CONFIDENTIAL其他线程函数 pthread_self()/返回自身标识符 pthread_equal()/判断是否同一线程 pthread_once()/使函数仅执行一次 2 0 0 5BAIDU CONFIDENTIALLinux线程库 目前广为使用的是:LinuxThreads 由Xavier Leroy实现(1996

17、)基本上实现POSIX 1003.1c 偏内核的混合实现模型2 0 0 5BAIDU CONFIDENTIALone to one模型 LinuxThreads使用one to one的方式 一个线程对应一个核心轻量进程 线程创建、取消、调度等由内核处理 线程同步等由用户空间的库处理 特点:充分利用内核的功能,但需要模拟内核不支持的特性2 0 0 5BAIDU CONFIDENTIAL线程创建 使用clone()系统调用(Linux特有),类似于fork(),但多个进程可共享数据。多个线程(进程)共享数据包括:虚拟地址空间(CLONE_VM)文件系统信息(CLONE_FS)打开文件表(CLON

18、E_FILES)信号处理方式(CLONE_SIGHAND)2 0 0 5BAIDU CONFIDENTIALGlibc库对线程的支持 大部分的函数已经重写或者包装为可重入函数,errno是线程级变量(宏)一部分函数提供了可重入版本(*_r)2 0 0 5BAIDU CONFIDENTIAL管理线程 库代码自动提供一个管理线程,负责子线程的创建和清理。第一次调用pthread_create()时,管理线程被创建。其他线程通过管理管道和管理线程通信,所有生成线程请求由管理线程完成。线程终止,管理线程负责清理其空间。2 0 0 5BAIDU CONFIDENTIAL引入管理线程的目的 某个线程收到信

19、号或exit退出,同一进程的其他线程也要退出。需要管理线程维护线程表并负责杀死其他线程。线程终止时无法自己回收栈空间,需要利用管理线程来完成。线程退出,需要管理线程来调用wait()/waitpid()以防止其变为僵尸进程 2 0 0 5BAIDU CONFIDENTIAL最大线程数的限制 受几个方面因素的约束:库中PTHREAD_THREADS_MAX缺省定义为1024 系统和用户最大进程数限制(ulimit)线程栈地址空间限制。缺省8M,2G地址空间(heap上)只能容纳最多256个 请不要预期创建线程一定会成功2 0 0 5BAIDU CONFIDENTIALLinuxThreads的缺

20、点 不完全遵循POSIX标准(信号处理、PID)引入管理线程带来潜在问题,低效率 在不支持实时信号的系统上需要占用SIGUSER1和SIGUSER2 使用信号来实现同步原语,效率低,现象怪 SIGSTOP(ctrl+z)和SIGCONT动作不能实现 nice等系统调用只影响调用线程 2 0 0 5BAIDU CONFIDENTIALNPTL Native Posix Thread Library,RedHat公司开发 依赖于Linux Kernel 2.6 线程各方面性能优于LinuxThreads POSIX兼容性优于LinuxThreads 1to1模型,没有管理线程,实现简单2 0 0

21、5BAIDU CONFIDENTIAL线上机器配置 redhat 8.0发行版32位系统,内核2.4.31,glibc-2.2.93(linuxthreadslinuxthreads-0.10)redhat AS4发行版64位系统,内核2.6.9,glibc-2.4.3(nptlnptl-2.4.3)32位兼容,glibc-2.2.93(linuxthreadslinuxthreads-0.10),从redhat 8.0发行版拷贝而来,位于/lib/rh80目录2 0 0 5BAIDU CONFIDENTIAL线程编程模型 线程池模型 直接在程序启动时起一池线程提供服务,共享数据通过加线程锁来

22、同步 简单并且高效,适合大部分应用情况 服务线程池里的线程如何获取客户端请求 所有服务线程同时accept pendingpool2 0 0 5BAIDU CONFIDENTIAL多线程程序GDB GDB都会断到设置的断点,并且会告诉你被断的是哪一个线程 info thread命令可以打印出进程里所有的线程 thread THREAD_NO命令可以切换到指定线程 GDB支持在设置断点时指定线程号,也就是只有指定的线程在到断点时会断2 0 0 5BAIDU CONFIDENTIAL常见问题:多线程程序GDB 除了当前线程之外,其它线程的执行是不可控的 GDB默认对于多个线程要停全停、要跑全跑 有

23、时会遇到程序停不住、跳到其它线程的情况 可能其它线程到了一个断点,也可能由于进程收到了某些信号2 0 0 5BAIDU CONFIDENTIALSMP/CMP与多线程 SMP(simultaneous multiprocessor,对称多处理器)CMP(chip multiprocessor,即我们通常说的多核处理器)多线程可以充分利用SMP/CMP 在SMP或CMP下由于并发度高,数据冲突的问题很容易重现2 0 0 5BAIDU CONFIDENTIALResources man手册 多线程多线程FAQ LinuxThreads/NPTL源代码 http:/ http:/ 0 0 5BAIDU CONFIDENTIAL

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 技术资料 > 其他杂项

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知淘文阁网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号© 2020-2023 www.taowenge.com 淘文阁