C++多核高级编程 - 05 多线程
来源:互联网 发布:任炎的陀螺淘宝购买 编辑:程序博客网 时间:2024/06/08 15:48
一,什么是线程
线程是进程中可执行代码流的序列,它被操作系统调用,并在处理器或内核上运行。所有进程都有一个主线程,主线程是进程的控制流或执行路线。线程分为用户线程和系统线程,线程在创建,维护和管理方面给系统带来的负担要轻得多。线程用于执行系统中的并发任务,可以简化系统中固有的并发的程序的结构。
用户级线程和内核级线程
线程有三种实现模型: 用户级或应用程序级,内核级,用户级和内核级混合线程。
他们之间主要的区别在于他们的模式以及要指派给处理器的线程的能力。
用户模式下,线程驻留在用户空间,是执行程序或连接库中的指令,由库调度器进行调度。
内核模式下,线程驻留在系统空间,可以进行系统调用,由操作系统调度器调度。
用户线程在运行时,任意给定时刻,每个进程只有一个线程在运行,而且只有一个处理器内核被分配给该进程。运行时调度库从进程的多个线程中选择一个,然后该线程和进程允许的一个内核线程关联起来,用户级线程是一种多对一的线程映射。
内核级线程驻留在内核空间,它们是内核对象,由操作系统调度器管理。有了内核线程,每个用户线程被映射或绑定到一个内核线程。 用户线程在其生命期内都会绑定到该内核线程。一旦用户线程终止,两线程都将离开系统。从内核线程到用户线程是一种一对一映射。
混合线程是用户线程和系统线程的交叉,使得运行库和操作系统都可以管理线程。在这种实现中进程有自己的内核线程池。可运行的用户线程由运行时库分派并标记为准备好执行的可用线程。操作系统选择用户线程并将它映射到线程池中可用的内核线程。
线程的上下文
操作系统管理很多进程的执行。它们来着不同的程序或系统。当一个进程从内核中移出,另一个进程成为活动的,这些进程之间便发生了上下文切换。操作系统必须记录重启进程和启动新进程使之活动所需要的所有信息。线程也有相同的处理方式。上下文保存的内容。
- 文件描述符
- 读/写 指针X 有关事件和信号的信息X 寄存器组
- 栈指针
- 指令计数器
- 诸如此类XX
二, 线程和进程的比较
线程和进程都能提供并发的程序执行,在决定使用进程或线程时可以从上下文切换开销,吞吐量,实体间通信,程序简化等方面进行考虑。
- 上下文切换:如果只有一个处理器,线程的上下文切换的开销较小。
- 吞吐量:使用多个线程可以增加程序的吞吐量,否则只有一个线程时,线程的IO将使整个程序被阻塞。
- 实体间通信:线程与同一进程间其他线程通信时不要求特殊的通信机制,可以直接进行数据的传输。进程间则必须建立和维护它们之间的通信机制。
- 破坏进程的数据:线程可以很轻松的破坏整个进程的数据。进程有自己的地址空间,相互隔离,数据也受到保护。
- 删除整个进程:线程出错时可以导致整个进程的终止,它导致的错误往往比进程导致的错误代价更大。
- 重用性:线程依赖于进程,不能从它所属的进程分离,不可以直接被重用,进程则更加的独立。
三, 设置线程属性
有些线程的信息可以描述线程的上下文切换,这些信息可以用于重建线程的环境。
同一进程中不同线程的主要区别是 id,定义线程状态的寄存器组,优先级和栈。
POSIX定义了线程属性对象,它封装了线程属性的一个子集,使用户可以方便的创建访问和更改。
- 竞争范围 (系统范围,进程范围)
- 栈大小
- 栈地址
- 分离状态 (是否从它的创建者中分离出来,在终止或退出时是否同其他对等线程或主线程同步)
- 优先级
- 调度策略和参数
四, 线程的结构
与进程一样,线程也有自己的上下文和属性。进程有代码段,数据段和栈段,线程同进程共享代码段和栈段。进程通常从内存的高地址开始,向下增长,线程则以下一个线程开始的地址为边界。
线程有着和进程一样的状态和状态转换关系。主线程可以决定整个进程的状态。对于多个线程的进程,只有所有的线程都处于休眠状态整个进程才处于休眠状态。
线程在系统中有着两种竞争范围:进程竞争和系统竞争。
进程竞争:线程与相同进程内的其他线程进行竞争。
系统竞争:线程与系统范围内的进程的线程进行竞争。
举例:进程A有4个线程,进程B有2个线程,系统有8个CPU,进程A的前3个线程使用CPU 0,1 在进程范围内竞争。进程B的一个线程和进程A的一个线程公用一个CPU,在系统范围内进行竞争。
由于竞争,线程也有着相关的调度策略。进程的调度策略和优先级属于主线程。每个线程可以有着和主线程不同的调度策略和优先级。
调度策略分为轮询(RR)和先进先出(FIFO)两种。
五, 创建线程
下面将用一个简单的示例来说明线程的创建和管理的方式。
这种时序图可以较好的描述线程的启动和终止的时间上的关系。
下面的程序将创建一个多线数组,并使其运行。
using namespace std;#include <iostream>#include <pthread.h>void *task(void* X){ int *Tmp; Tmp = static_cast<int *>(X); for(int Count = 0; Count < *Tmp; Count++) { cout << "Work from thread: " << Count << endl; } cout << "Thread complete" << endl; return NULL;}int main(int argc, char *argv[]){ int N = 10; pthread_t MyThread[10]; for(int Count = 0; Count < 10; Count++) { pthread_create(&MyThread[Count], NULL, task, &N); } for(int Count = 0; Count < 10; Count++) { pthread_join(MyThread[Count], NULL); } return 0;}
六, 管理线程
这里将介绍pthread中关于线程管理的一些函数和管理方式。
(1) 创建线程
int pthread_create(pthread_t* restrict thread, const pthread_attr_t *restrict attr, void*(*start_routine), void *restrict arg)
pthread_t* restrict thread: 描述线程ID
const pthread_attr_t *restrict attr: 新线程的专有属性,如果设置为NULL则使用默认值
void*(*start_routine): 新线程将要执行的指令函数
void *restrict arg: 指令函数需要的参数
关键字restrict: 与之前的IEEE标准保持一致
(2) 结合线程
int pthread_join(pthread_t thread, void** value_ptr)
用于结合或再次结合进程中的控制流,pthread_join调用将导致调用线程执行挂起,直到目标线程终止。它类似于进程中的wait()函数,该函数由线程的创建者调用,该调用线程等待新线程终止并返回,然后再次结合到调用线程的控制流中。如果线程控制句柄可以公共访问,该函数也可以被对等线程调用。使得任何线程可以将控制流同进程中的任何其他线程结合。如果调用线程在目标线程返回前被取消,则会导致目标线程成为僵死线程。
pthread_t thread: 描述线程ID
void** value_ptr: 如果目标线程返回成功,该参数保存线程的退出状态。
(3) 获得线程ID
pthead_t pthread_self(void)
线程被创建后,该函数返回线程ID。
自终止:int pthread_exit(void** value_ptr)
当终止线程调用pthread_exit()后,它在 value_ptr中得到该线程的退出状态。退出状态被返回到pthread_join中。当调用这个函数时,线程所使用的资源不会被释放。
终止对等线程:int pthread_cancel(pthread_t thread)
应用程序中可能有线程监视其他线程的工作,如果发现有的线程执行不力或不再需要时,有必要使其终止。pthread_cancle的调用是取消一个对等线程的请求。这个请求可能立刻被同意,稍后被同意,甚至被忽略。目标线程可能立刻终止,稍后终止,或拒绝终止。
在取消一个对等线程的请求被同意时,会有一个取消过程,目标线程的取消状态(可取消 or 不可取消)和 取消类型(取消何时发生)决定了收到取消请求后线程继续执行的能力。
设置取消状态和取消类型:
pthread_setcancelstate(int stat, int *oldstat);
pthread_setcanceltype(int type, int* oldtype);
使用取消点
当推迟取消请求时,线程的终止会推迟到线程的函数执行后期。当取消发生时,应该是安全的,因为它不处于互斥量加锁,执行关键代码,令数据处于某种不可用状态的情况中。代码执行中,这些位置是很好的取消点。取消点是一个检查点,线程在这里检查是否有任何取消请求未决,如果有,则终止。
void pthread_testcancel(void);
另外可利用安全取消的库函数和系统调用
- pthread_cond_wait()
- pthread_timewait()
- pthread_join()
当线程因调用这些函数而阻塞时,取消线程是安全的。
(4) 管理线程栈
int pthread_attr_setstacksize( pthread_attr_t *attr, size_t stacksize);
int pthread_attr_getstacksize( pthread_attr_t *attr, size_t *stacksize);
int pthread_attr_setstackaddr( pthread_attr_t *attr, void *stackaddr);
int pthread_attr_getstackaddr( pthread_attr_t *attr, void **stackaddr);
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);
int pthread_attr_getstack(pthread_attr_t *attr, void **stackaddr, size_t *stacksize);
(5) 管理线程调度和优先级
int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched);
int pthread_attr_getinheritsched(pthread_attr_t *attr, int *inheritsched);
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
int pthread_attr_getschedpolicy(pthread_attr_t *attr,int *policy);
int pthread_attr_setschedparam(pthread_attr_t *attr,const struct sched_param *param);
int pthread_attr_getschedparam(pthread_attr_t *attr,struct sched_param *param);
(6) 管理线程竞争范围
int pthread_attr_setscope(pthread_attr_t *attr, int contentionscope);
int pthread_attr_getscope(const pthread_attr_t *restrict attr, int *restrict contentionscope);
七, 扩展线程接口
线程的接口类将把线程管理等功能进行封装,使得线程更易使用,功能更强,也更加灵活。
程序共4部分:
thread_object.h:定义接口类。
thread_object.cpp:接口类的实现
thread_filter.cpp:派生自接口类基类,实现简单的测试功能。
main.cpp:测试入口程序。
thread_object.h
#ifndef __THREAD_OBJECT_H#define __THREAD_OBJECT_Husing namespace std;#include <iostream>#include <pthread.h>#include <string>class thread_object{public: thread_object(); ~thread_object(); void setPriority(int nPriority); void setSchedPolicy(int nPolicy); void setContentionScope(int nScope); void setDetached(void); void setJoinable(void); void name(string strName); void run(void); void join(void); friend void *thread(void *buff);private: pthread_t m_Tid;protected: virtual void do_something(void) = 0; pthread_attr_t m_attr_SchedAttr; struct sched_param m_attr_SchedParam; string m_Name; int m_nNewPolicy; int m_nNewState; int m_nNewScope;};class filter_thread: public thread_object{protected: void do_something(void);public: filter_thread(void); ~filter_thread(void);};#endif
thread_object.cpp
#include "thread_object.h"thread_object::thread_object(){ pthread_attr_init(&m_attr_SchedAttr); pthread_attr_setinheritsched(&m_attr_SchedAttr, PTHREAD_EXPLICIT_SCHED); m_nNewState = PTHREAD_CREATE_JOINABLE; m_nNewScope = PTHREAD_SCOPE_PROCESS; m_nNewPolicy = SCHED_OTHER;}thread_object::~thread_object(){}void thread_object::join(void){ if (m_nNewState == PTHREAD_CREATE_JOINABLE) { pthread_join(m_Tid, NULL); }}void thread_object::setPriority(int nPriority){ int nPolicy; struct sched_param stParam; stParam.sched_priority = nPriority; pthread_attr_setschedparam(&m_attr_SchedAttr, &stParam);}void thread_object::setSchedPolicy(int nPolicy){ if (nPolicy == 1) { pthread_attr_setschedpolicy(&m_attr_SchedAttr, SCHED_RR); pthread_attr_getschedpolicy(&m_attr_SchedAttr, &m_nNewPolicy); } if (nPolicy == 2) { pthread_attr_setschedpolicy(&m_attr_SchedAttr, SCHED_FIFO); pthread_attr_getschedpolicy(&m_attr_SchedAttr, &m_nNewPolicy); }}void thread_object::setContentionScope(int nScope){ if (nScope == 1) { pthread_attr_setscope(&m_attr_SchedAttr, PTHREAD_SCOPE_SYSTEM); pthread_attr_getscope(&m_attr_SchedAttr, &m_nNewScope); } if (nScope == 2) { pthread_attr_setscope(&m_attr_SchedAttr, PTHREAD_SCOPE_PROCESS); pthread_attr_getscope(&m_attr_SchedAttr, &m_nNewScope); }}void thread_object::setDetached(void){ pthread_attr_setdetachstate(&m_attr_SchedAttr, PTHREAD_CREATE_DETACHED); pthread_attr_getdetachstate(&m_attr_SchedAttr, &m_nNewState);}void thread_object::setJoinable(void){ pthread_attr_setdetachstate(&m_attr_SchedAttr, PTHREAD_CREATE_JOINABLE); pthread_attr_getdetachstate(&m_attr_SchedAttr, &m_nNewState);}void thread_object::run(void){ pthread_create(&m_Tid, &m_attr_SchedAttr, thread, this);}void thread_object::name(string strName){ m_Name = strName;}void* thread(void* buff){ thread_object* pThread; pThread = static_cast<thread_object *> (buff); pThread->do_something(); return NULL;}
thread_filter.cpp
<pre name="code" class="cpp">#include "thread_object.h"filter_thread::filter_thread(void){ pthread_attr_init(&m_attr_SchedAttr);}filter_thread::~filter_thread(void){}void filter_thread::do_something(void){ struct sched_param stParam; int nPolicy; pthread_t thread_id = pthread_self(); string strSchedule; string strState; string strScope; pthread_getschedparam(thread_id, &nPolicy, &stParam); switch (m_nNewPolicy) { case SCHED_RR: strSchedule.assign("RR"); break; case SCHED_FIFO: strSchedule.assign("FIFO"); break; case SCHED_OTHER: strSchedule.assign("OTHER"); break; default: strSchedule.assign("unknown"); break; } switch (m_nNewState) { case PTHREAD_CREATE_DETACHED: strState.assign("DETACHED"); break; case PTHREAD_CREATE_JOINABLE: strState.assign("JOINABLE"); break; default: strState.assign("unknown"); break; } switch (m_nNewScope) { case PTHREAD_SCOPE_PROCESS: strScope.assign("PROCESS"); break; case PTHREAD_SCOPE_SYSTEM: strScope.assign("SYSTEM"); break; default: strScope.assign("unknown"); } cout << m_Name << " : " << thread_id << endl << "--------------------------------------"<< endl << " priority : " << stParam.sched_priority << endl << " policy : " << strSchedule << endl << " state : " << strState << endl << " scope : " << strScope << endl << endl;}
#include "thread_object.h"#include <unistd.h>int main(int argc, char * argv[]){ filter_thread MyThread[4]; MyThread[0].name("Proteus"); MyThread[0].setSchedPolicy(2); MyThread[0].setPriority(7); //MyThread[0].setDetached(); // 若设置为Detached则在打印时会出现乱序现象 MyThread[1].name("Stand Along Complex"); MyThread[1].setContentionScope(1); MyThread[1].setPriority(5); MyThread[1].setSchedPolicy(2); MyThread[2].name("Krell Space"); MyThread[2].setPriority(3); MyThread[3].name("Cylon Space"); MyThread[3].setSchedPolicy(2); MyThread[3].setPriority(2); for (int i = 0; i < 4; i++) { MyThread[i].run(); MyThread[i].join(); } return 0;}
- C++多核高级编程 - 05 多线程
- C/C++_多核多线程编程_互斥锁
- 多核多线程技术编程
- 多核多线程技术编程
- 多线程的多核编程
- c#Task多核编程
- linux Kernel 多核多线程编程
- window下多核多线程编程
- C++多核高级编程 - 04 多进程
- C++多核高级编程 ' 第3章
- 《C++多核编程》 第六章 多线程
- 并行程序设计-Windows多线程编程-多核1
- 跨平台多核多线程编程指南
- 多核多线程编程的一些基础知识
- 单核多线程和多核编程的区别
- 多线程高级编程
- python高级编程--多线程
- Objective-C高级编程--多线程和内存管理笔记
- rails gem开发实录之cancan的使用
- Linux下的函数执行时间的统计方法
- C# 中的委托和事件
- MFC基础学习(1)---对话框的创建
- C++多核高级编程 - 04 多进程
- C++多核高级编程 - 05 多线程
- 条款36:绝不重新定义继承而来的non-virtual函数
- 探索流程的奥秘之二: 流程的步骤是什么东东
- 第十三周上机任务2
- cookie
- careercup5.1
- 十大安全评估工具
- 第十三周上机任务3
- 勉强和贪婪的正则匹配的工作方式