多线程
多线程定义
一个进程可以包含多个线程,所有线程都执行同一个程序。这些线程共享相同的全局内存(数据段和堆段),但每个线程都有自己的堆栈用于存储各自的局部变量。
使用多线程
要使用多线程,需要引用#include <pthread.h>
头文件,且编译时候需要链接libpthread.so
库。
具体编译指令:
gcc pthread_test.c -o pthread_test -lpthread
线程属性
线程属性:绑定属性、分离属性、调度属性、堆栈大小属性、满栈警戒区大小属性。
int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destory(pthread_attr_t *attr);
分离属性
线程是能够合并和分离的,分离属性则是在线程
创建之前,就线程属性设置为分离。如果调用了,则没必要调用pthread_join()
和pthread_detach()
回收线程资源了。
线程分离的接口定义:
pthread_attr_setdetachstat(pthread_attr_t *attr, int detachstate);
// detachstate: PTHREAD_CREATE_DETACHED(分离的) \ PTHREAD_CRESTE_JOINABLE(可合并的,默认属性)
具体工程代码
#include <pthread.h>
int main(int argc, char *argv[])
{
phtread_attr_t attr;
pthread_t thread;
// ...
pthread_attr_init(&attr);
pthread_attr_setdetachstat(&attr, PTHREAD_CREATE_DETACHED);
phtread_create(&th, &attr, thread, NULL);
}
堆栈大小属性
要知道线程的主函数与main函数中的主线程,分别有自己的堆栈。虽然同一个进程的线程之间是共享内存空间,但每个线程分别拥有自己独立的堆栈用于存放各自的局部变量。Linux默认为每个线程分配8MB的堆栈空间。可通过pthread_attr_setstacksize()
进行扩容。
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
// stacksize: 堆栈大小,以字节为单位。线程堆栈小于16kb,尽量按照内存页面大小整倍数分配。[4KB(32位系统)或2MB(64位系统].够用尽量不用乱动。
创建线程
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);
// thread:线程句柄
// attr:线程属性,若使用线程默认属性可传NULL,也可上面的介绍的线程属性进行修改。
// start_routine:线程入口函数
// arg: 线程函数的参数
// return: 0,代表成功,其它值代表创建失败
退出线程
线程函数结束时,调用pthread_exit
可以安全退出
void pthread_exit( void* retval );
// retval:pthread_exit 将通过 retval 参数向线程的回收者传递其退出信息
结束线程
pthread_join()函数,以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。如果线程已经结束,那么该函数会立即返回。并且thread指定的线程必须是joinable的。
需要注意的是,如果主线程中没有调用phtread_join
,一旦主线程很快结束,将会直接使整个进程结束。使得创建的线程还没开始就被迫结束。解决这个问题,可以将线程进行分离。或者在主线程调用phtread_join
,阻塞线程,直到被回收的线程结束。
int pthread_join(pthread_t thread, void **retval);
// thread:线程ID
// retval:用户定义的指针,用来存储被等待线程的退出返回值。
// return: 0,返回成功,失败返回错误码
// EDEADLK (error : dead lock) 可能引起死锁。如两个线程互相针对对方调用pthread_join,或线程对自身调用 pthread_join
// EINVAL (error : invalid) 目标线程是不可回收的,或者已经有其他线程在回收该目标线程
// ESRCH 目标线程不存在
线程分离
线程分离是将线程资源的回收工作交由系统自动来完成,也就是说当被分离的线程结束之后,系统会自动回收它的资源。
创建子线程前分离
可以通过上面的介绍的分离属性pthread_attr_setdetachstat(&attr, PTHREAD_CREATE_DETACHED);
子线程中分离
pthread_detach(pthread_self());
主线程设置子线程分离
pthread_detach(pid)
pid为子线程的线程号
注意一旦子线程设置为线程分离,是不能调用pthread_join函数的
int pthread_detach(pthread_t thread);
// return :
// EINVAL :thread 不是 joinable thread。
// ESRCH :找不到线程ID所标识的线程。