作者:UUUUUUUUUU8 | 来源:互联网 | 2023-09-14 15:30
Linux 系统编程 学习:009-线程
背景
我们在此之前完成了 有关进程的学习。从这一讲开始我们学习线程。
在Linux 系统编程 学习:有关概念中,我们介绍了线程和进程的概念。
概念
我们知道:
- 线程是cpu或操作系统调度的基本单位。线程大部分的资源是共享的,仅仅申请了自己的栈、空间。
- 线程是进程内部的一个执行分支,线程量级很小。
- 在程序中创建线程,可以提高效率,进程内线程越多,争夺到CPU的概率就越大,执行代码的概率就越大(有一个度)。
- 线程可以解决很多问题,而不会像进程一样有那么多的开销。
- 在线程中需要注意同步的问题。
线程开发基本步骤
#if 0
为什么要用线程
http://blog.csdn.net/huangyimo/article/details/46756151
线程比进程更快,开销更小。
1.当需要在在进程间共享数据时,我们必须使用IPC来进行通信,因为两个进程间没有“天生”的共享内存区(进程要求虚拟空间独立,如果想共享数据,必须把这几个进程的某个地址映射到相同的物理内存上)。IPC的使用将会导致开销上升。
2.需要创建新进程时,我们使用fork()函数。虽然该函数不会拷贝所有的数据,但是最基本的数据,比如页表,文件描述符表等还是需要创建的。这也会导致开销上升。
而线程完全不会有上面的问题。两个线程之间的数据共享是很自然的事,因为同一进程的线程都在一个虚拟地址空间,数据的共享就像变量赋值一样(但要注意同步问题)。线程的创建也比进程的创建快,因为它不需要复制和创建很多数据。
一个线程的bug很可能会引起该进程的崩溃(所有的线程都在同一个虚拟地址空间内,一个线程能很容易的访问到另一个线程的空间)
线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。(调度的最小单位)
http://blog.csdn.net/suool/article/details/38542543
对于内核来说,没有线程和进程的区分(但是对于用户来说,是有区别的)
每个进程在创建的时候都申请了新的内存空间以存储代码段\数据段\BSS段\堆\栈空间,并且这些的空间的初始化值是父进程空间的,父子进程在创建后不能互访资源.
而每个新创建的线程则仅仅申请了自己的栈,空间,与同进程的其他线程共享该进程的其他数据空间包括代码段\数据段\BSS段\堆以及打开的库,mmap映射的文件与共享的空间,使得同进程下的线程共享数据十分的方便,只需借助这些共享区域即可,但也有问题即是同步问题.
线程开发的基本步骤:
#include
1. 声明以及初始化一个线程属性变量,并通过函数设置线程属性
(当不需要设置线程属性的时候,可以在后续参数的pattr部分填入NULL)
pthread_attr_t pattr; //如果不需要设置线程属性,则可以不使用这2条语句
pthread_attr_init(&pattr); //如果不需要设置线程属性,则可以不使用这2条语句
//通过函数设置线程属性
pthread_attr_getaffinity_np pthread_attr_getdetachstate
pthread_attr_getguardsize pthread_attr_getinheritsched
pthread_attr_getschedparam pthread_attr_getschedpolicy
pthread_attr_getscope pthread_attr_getstack
pthread_attr_getstackaddr pthread_attr_getstacksize
pthread_attr_setaffinity_np pthread_attr_setdetachstate
pthread_attr_setguardsize pthread_attr_setinheritsched
pthread_attr_setschedparam pthread_attr_setschedpolicy
pthread_attr_setscope pthread_attr_setstack
pthread_attr_setstackaddr pthread_attr_setstacksize
其中,用得最多是关于线程的可分离性。
// 如果一个进程是分离的,则意味着在线程结束以后,将被系统回收(常用在无反馈的线程中)
//(而不是变成僵尸来等待 pthread_join 函数的回收)
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
/*
设置线程的可分离性到属性结构体,成功返回0,失败返回errorno
pthread_attr_t : 用于赋值的属性结构体
detachstate : 设置可分离性
PTHREAD_CREATE_DETACHED 分离的。
PTHREAD_CREATE_JOINABLE 不可分离的,可汇接的(默认值)
可汇接的线程在结束时,可以使用以下方式返回值给其他线程
void pthread_exit(void *retval);
“ 多线程的进程就好比一条河流中的多条支流,支流的终点,将重新汇聚在一起。
————叔超硕徳
*/
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
// 获取线程属性结构体中的可分离性
2. 以指定的线程属性#attr(不需要设置线程属性时,默认填入NULL)创建一个线程#start_routine,并传入参数#arg,成功时,赋线程ID到#thread中,返回0,失败返回error number
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
/*
pthread_t tid;
pthread_create(&tid,NULL,(void *)start_routine,NULL);
*/
3. 根据情况来决定是否等待线程的返回值,如果需要等待,以下面的方式进行
int pthread_join(pthread_t thread, void **retval);
// 阻塞等待一个线程ID为#thread的不可分离线程的结束,将其返回值传入#reval
// 成功返回0,失败返回error number
一个线程所使用的内存资源在应用pthread_join调用之前不会被重新分配,所以对于每个线程必须调用一次pthread_join函数(被分离线程除外)。
4. 可以根据情况提前取消一个线程的执行
int pthread_cancel(pthread_t thread);
5. 不再使用某个线程属性结构体时,应该进行销毁
pthread_attr_destroy(&pattr);
#endif
#include
#include
#include
void *start_routine(void *arg)
{
int count = 5;
while(count--)
{
sleep(1);
printf("int child thread arg=%s\n", (char *)arg);
}
return "world"; //pthread_exit("world");
}
int main(void)
{
pthread_t tid;
char *string = "hello";
void *retval;
pthread_attr_t pattr; // 线程属性结构体 使用前应该初始化(pthread_attr_init )
pthread_attr_init(&pattr);// 结束后应该回收资源解除初始化(pthread_attr_destroy )
pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_DETACHED);// 设置线程的可分离性
errno = pthread_create(&tid, &pattr, start_routine, string); // 创建线程start_routine,为线程传参数string
if(errno != 0)
{
fprintf(stderr, "creat thread failed:%s\n", strerror(errno));
return -1;
}
pthread_attr_destroy(&pattr);
printf("in parent thread\n");
pthread_join(tid, &retval); // 等待线程的结束,并取返回值
printf("retval = %s\n", (char *)retval);
return 0;
}