多线程

一、多线程的优点

1.多个任务由多个线程去执行,而不是等待上一个任务执行完之后再开始执行,看起来就像一起在执行一样,大大提高用户体验。

2.充分利用了多核处理器。

3.更高效的通信。同一进程的线程共享进程的公共资源,共享同一内存空间,这样线程之间通信会更方便和高效。

4.开销比进程小。创建线程和线程之间的上下文切换的开销比较小,因为线程本身几乎不拥有系统资源。

二、线程一些相关知识点

每个进程至少拥有一个线程(称为主线程),线程是程序的最小执行单位,是操作系统分配CPU时间的最小实体,一个进程的执行说到底是是进程里的某线程在执行。其它线程都是由主线程创建的。

线程是属于进程的,当进程退出时,该进程所产生的所有线程将被强制退出并清除。当在主线程里使用pthread_exit,可以使主线程退出但是进程并没有退出,这样其它线程仍然可以运行直至结束,然后进程再结束。

每个线程都有唯一的线程ID,线程结束后ID就不存在了。

C++多线程开发有两种方式:利用POSIX的多线程API;C++自带的线程类。

三、线程的状态

就绪态:等待处理机

运行态:获得处理机

阻塞态

终止态:线程已经运行结束或被其它线程取消了,但其资源还没有被收回,而且可以重新复活,应当及时收回资源。

四、一些多线程函数(POSIX API)

1.创建线程

int pthread_create(pthread_t* pid,const pthread_attr_t* attr,void*(*func)(void*),void*arg);//参数分别表示:线程ID,线程属性,线程函数,线程函数参数

属性NULL表示默认属性,属性可以修改;线程函数参数可以为NULL。

获取线程pid可以用int pthread_self()函数

2.等待线程结束

int pthread_join(pthread_t pid,void** value_ptr);//参数表示:线程ID,线程函数返回值

为阻塞函数,会等待子线程结束。同时让子线程占用的资源释放。

3.线程属性

属性信息包括线程分离状态、调度策略、作用域、栈尺寸、优先级等。

1)获取属性

#define _FNU_SOURCE//使用下面的函数需定义此行且在头文件前面
#include <pthread.h>
int pthread_getaddr_np(pthread_t pid,pthread_attr_t* attr);//attr传进函数,用于保存属性

int pthread_attr_deatroy(pthread_atrr_t* attr);//使用了上面的函数后需要使用这个函数来销毁属性,从而释放资源

2)属性一:分离状态

线程要么是分离状态,要么是非分离状态(也叫可连接,joinable),前者用宏PTHREAD_CREATE_DETACHED,后者用PTHREAD_CREATE_JOINABLE。默认情况为可连接状态,可连接状态即表示该线程可以被其它线程回收资源或杀死,并不会主动释放资源,必须由其它线程来回收,线程自己调用pthread_exit并不会销毁资源,只能由其它线程调用pthread_join来销毁。如果父进程不调用pthread_join且提前子线程退出,子线程会被init进程所收养,init称为它的父进程,将调用wait系列函数来回收资源,即这种情况不会资源泄露。一个线程不能被多个线程等待,即pthread_join,否则第二个pthread_join会产生错误代码ESRCH,表示没有此线程。

可分离状态即是线程结束后,其资源立刻由系统回收而不用父进程管,而且线程不能被其它线程杀死或取消。

设置为可分离状态有两种方

//方法一:

//先初始化线程属性
int pthread_attr_init(pthread_attr_t* attr);
//将属性代入下面函数
int pthread_attr_setdetachstate(pthread_attr_t* attr,int detachstate);//detachstate可取上面的两个宏,第一个表示可分离状态,第二是可连接状态
//然后再代入下面函数
int pthread_create(pthread_t* pid,const pthread_attr_t* attr,void*(*func)(void*),void*arg);

方法二

//将一个可连接线程转为可分离线程
int pthread_detach(pthread_t pid);

查看线程的分离状态属性

int pthread_getattr_np(pthread_t pid,pthread_attr_t* attr);//先获取线程属性放在attr中,再传给下面的函数,获取分离属性放在detachstate指向的int型变量中。
int pthread_attr_getdetachstate(pthead_attr_t* attr,int* detachstate);//

3)属性二:栈尺寸

获取栈尺寸

//先使用pthrad_getattr_np获取属性信息放在attr中,再传给下面的函数,获取栈尺寸放在stacksize中
int pthread_attr_getstacksize(pthread_attr_t* attr,size_t* stacksize)

4.线程的结束

1)主动结束

void pthread_exit(void* retval);//retval为返回给主线程的值,可以为NULL.
//主线程使用pthread_join 来获取返回值

2)被动结束

其它线程使用pthread_kill发送信号给线程,线程可以使用signal函数来处理信号,若无signal函数,则按默认方式处理,比如SIGQUIT,若不处理,则会使整个进程结束。

int pthread_kill(pthread_t pid,int signal);//当signal为0时,一般用于检测线程是否在运行,执行成功返回0,错误返回错误码,ESRCH表示线程不存在,EINVAL表示信号不合法

也可以使用pthread_cancel来请求某线程终止运行,注意,就算成功执行函数也不一定意味着线程终止了,只有被取消线程自己调用一些系统调用或者库函数的时候才会真正取消结束运行,比如printf、read write等,或者被取消线程自己调用pthread_testcancel(让内核自己去检测是否需要取消当前线程)时才会真正取消。取消成功返回PTHREAD_CANCLED(-1).

int pthread_cancel(pthread_t pid);

5.线程退出时的清理工作

外界取消是不可预知的,需要一些函数来保证线程顺利的释放了资源,特别是锁资源。

//以下两个函数成对出现
void pthread_cleanup_push(void (*func)(void*),void* arg);//把一个函数压入栈
void pthread_cleanup_pop(int execute);//execute为0表示栈函数不用执行

栈的函数何时执行:

1)主动退出时:return,pthread_exit

2)pthread_cleanup_pop,且execute非0

3)线程被其它线程取消时,即pthread_cancel

五、C++线程类

1.默认构造函数

thread();//线程不会马上执行

2.thread(线程函数名,线程函数参数...)//线程开始执行

3.等待线程结束

void join();//销毁资源

4.将线程进行分离

void detach();

5.移动构造函数move

thread t1(func1,n);

thread t2(move(t1));//t1线程不会运行,t2运行,t1的线程函数移动给了t2。

 

posted @ 2020-06-08 12:40  江雨牧  阅读(325)  评论(0)    收藏  举报