| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> C++知识库 -> 学习关于线程 -> 正文阅读 |
|
[C++知识库]学习关于线程 |
一.线程的概念 有些情况需要在一个进程中同时执行多个控制流程,比如实现一个图形界面的下载软件,一方面需要和用户交互,等待和处理用户的鼠标键盘事件,另一方面又需要同时下载多个文件,等待和处理从多个网络主机法来的数据,这些任务都需要一个“等待->处理”的循环,那么如何才能同时进行多项任务? 1.需要线程:是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。 2.由于同一个进程的多个线程共享同一地址空间,因此代码块,数据块都是共享的,如果定义一个函数,在各线程中都可以调用,如果定义一个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境: ①文件描述符表 ②每种信号的处理方式 ③当前工作目录 ④用户id和组id 但有些资源是每个线程各有一份的: ①线程id ②上下文,包括各种寄存器的值,程序计数器和栈指针 ③栈空间 ④errno变量 ⑤信号屏蔽字 ⑥调度优先级 在Linux上线程函数对于libpthread共享库,在编译时要加上-lpthread ②.线程控制 1.创建线程 返回值:成功返回0,失败返回错误号。以前学过的系统函数都是成功返回0,失败返回-1,而错误号保存再全局变量errno中。 pthread库函数都是通过返回值返回错误号,虽然每个线程也都有一个errno,但这是为了兼容其他函数接口而提供的,pthread库本身并不适用它,通过返回值返回错误码更佳清晰。 2.获得当前线程的id 返回值:总是成功返回,返回调用该函数线程ID
3.思考:主线程在一个全局变量ntid中保存了新创建的现场的id,如果新创建的线程不调用pthread_self而是直接打印这个ntid,能不能达到同样效果?
? 4.如果需要只终止某个线程而不终止整个进程,可以有三种方法? ①从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit。 ②一个线程可以调用pthread_cancel终止同一进程中的另一个线程。 ③线程可以调用pthread_exit终止自己。 ? value_ptr是void*类型,和线程函数返回值用法一样,其他线程可以调用pthread_join获得这个指针。 注意:pthread_exit或者return返回指向的内存单位必须是全局的或者是用malloc分配的,不能再线程函数的栈上分配(因为当其他线程得到这个返回指针,线程函数已经退出了) 5.调用该函数的线程将挂起等待,知道id为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的,如下: ①如果thread线程通过return返回,value_ptr所指向的单位里存放的是thread线程函数的返回值。 ②如果thread线程被别的线程调用pthread_cancel异常终止掉,value_ptr所指向的单元里存放的是常数PTHREAD_CANCLED。 ③如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。 ④如果对thread线程的终止状态不感兴趣,可以传NULL给value_ptr参数。
? 三.线程间同步 1.多个线程同时访问共享数据时可能会冲突,这跟前面讲信号时所说的可重入性是同样的问题。比如两个线程都要把某个全局变量增加1,这个从操作在某平台需要三条指令完成: 从内存读变量值到寄存器-->寄存器的值加1-->将寄存器的值写回内存
2.对于多线程的程序,访问冲突的问题很普遍,解决办法是引入互斥锁:Mutex(Mutual Exclusive Lock),获得锁的线程可以完成“读-修改-写”的操作,然后释放锁给其他线程,没有获得锁的线程只能等待而不能访问共享数据,这样“读-修改-写”三步操作组成一个原子操作,要么都执行,要么都不执行,不会执行到中间被打断,也不会在其他处理器上并行做这个操作。 pthread_mutex_init函数对mutex做初始化,参数attr设定mutex属性,如果attr为NULL表示缺省属性。 用pthread_mutex_init函数初始化的mutex可以用pthread_mutex_destrory销毁。 如果mutex变量是静态分配(全局变量或static变量),也可以用宏定义PTHREAD_MUTEX_INITIALIZER来初始化,相当于用pthread_mutex_init初始化并且attr参数为NULL。 3.mutex的加锁和解锁操作可以用下列函数: 返回值:成功返回0,失败返回错误号。 一个线程可以调用pthread_mutex_lock获得mutex,如果这时另一个线程已经调用pthread_mutex_lock获得该mutex,则当前线程需要挂起等待,知道另一个线程调用pthread_mutex_unlock释放mutex,当前线程被唤醒,才能获得该mutex并继续执行。 如果一个线程既想获得锁,又不想挂起等待,可以调用pthread_mutex_trylock,如果mutex已经被另一个线程获得,这个函数会失败返回EBUSY,而不会使线程挂起等待。
4.挂起等待和唤醒等待线程的操作如何实现? 每个mutex有一个等待队列,一个线程要在mutex上挂起等待,首先在把自己加入等待队列中,然后置线程状态为睡眠,再调用调度器函数切换到别的线程。一个线程要唤醒等待对队列中的其它线程,只需从等待队列中取出一项,把它的状态从睡眠改为就绪,加入就绪队列,那么下次调度器函数执行时就有可能切换到被唤醒的线程。 如果同一个线程先后两次调用lock,在第二次调用时,由于锁已经被占用,该线程会挂起等待别的线程释放锁,然而锁被自己占用着的,该线程又被挂起而没有机会释放锁,因此就永远处于挂起等待状态,这叫做死锁(deadlock)。 另一种死锁情况:线程A获得锁1,线程B获得锁2,这时线程A调用lock试图获得锁2,结果是需要挂起等待线程B释放锁2,而这时线程B也调用lock试图获得锁1,结果需要挂起等待线程A释放锁1,于是线程A和B都永远处于挂起状态。如果涉及到更多线程和更多锁,有没有可能死锁的问题就会变得复杂和难以判断。 写程序时应尽量避免同时获得多个锁,如果一定有必要这么做,一个原则: 如果所有线程在需要多个锁时都按相同的先后顺序获得锁,则不会出现死锁。比如一个程序用到锁1,锁2,锁3,它们对应的mutex变量是锁1->锁2->锁3,那么所有线程在需要同时获得2个或3个锁时都应该按锁1,锁2,锁3的顺序获得。 如果要为所有锁确定一个先后顺序比较困难,应尽量使用pthread_mutex_trylock调用代替pthread_mutex_lock调用,以免死锁。 5.线程间同步还有一种情况:线程A需要等某个条件成立才能继续往下执行,现在这个条件不成立,线程A就阻塞等待,而线程B在执行过程中使这个条件成立,就唤醒线程A继续执行。在pthread库中通过条件变量来阻塞等待一个条件,或者唤醒等待这个条件的线程。条件变量用pthread_cond_t类型的变量来表示,可以这样初始化和销毁: 返回值:成功返回0,失败返回错误号。 6.条件变量的操作可以用以下函数: 返回值:成功返回0,失败返回错误号。 pthread_cond_timedwait函数还有一个额外参数可以设定等待超时,如果到达abstime所指定的时刻仍然没有别的线程来唤醒当前线程,就返回TIMEDOUT,一个线程可以调用pthread_cond_signal唤醒在某个条件变量上等待另一个线程,也可以调用pthread_cond_broadcast唤醒在这个条件变量上等待的所有线程。
7.mutex变量非0即1,看作是一种资源可用数量,初始化时mutex是1,表示有一个可用资源,加锁时获得该资源,将mutex减到0,表示不再有可用资源,解锁时释放该资源,将mutex重新加到1,表示又有了一个可用资源。 8.信号量semaphore和mutex类似,表示可用资源数量,和mutex不同的是:这个数量可以大于1,这种信号量不仅课用于同意进程的线程间同步,也可用于不同进程间的同步。 semaphore变量类型是sem_t sem_init()初始化话一个semaphore变量,value参数表示可用资源的数量,pshared参数为0表示信号量用于同一进程的线程间同步。 在用完semaphore变量之后应调用sem_destroy()释放与semaphore相关的资源。 调用sem_wait()可以获得资源,使用semaphore值减1,如果调用sem_wait()时semaphore的值已经是0,则挂起等待。如果不希望挂起等待,可以调用sem_trywait(). 调用sem_post()可以释放资源,使semaphore值加1,同时唤醒挂起等待的线程。 ?
|
|
C++知识库 最新文章 |
【C++】友元、嵌套类、异常、RTTI、类型转换 |
通讯录的思路与实现(C语言) |
C++PrimerPlus 第七章 函数-C++的编程模块( |
Problem C: 算法9-9~9-12:平衡二叉树的基本 |
MSVC C++ UTF-8编程 |
C++进阶 多态原理 |
简单string类c++实现 |
我的年度总结 |
【C语言】以深厚地基筑伟岸高楼-基础篇(六 |
c语言常见错误合集 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/24 9:10:47- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |