Linux学习_线程的概念创建和终止
Posted Leslie X徐
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux学习_线程的概念创建和终止相关的知识,希望对你有一定的参考价值。
系统线程概念和实现
线程的概念
- 概念:
- 进程是资源管理的最小单位,线程是程序执行的最小单位
- 每个进程有自己的数据段,代码段和堆栈段。线程通常叫做轻型的进程,它包涵独立的栈和CPU寄存器状态,线程是进程的一条执行路径,每个线程共享其所附属进程的所有资源,包括打开的文件、内存页面、信号标识及动态分配的内存等。
- 因为线程和进程比起来很小,所以相对来说,线程花费更少的CPU资源。
- 在操作系统设计上,从进程演化出线程,最主要的目的就是更好地支持多处理器,并且减小进程上下文切换的开销。
- 进程和线程关系
- 线程和进程的关系是:线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一用户内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除。一个进程至少需要一个线程作为他的指令执行体,进程管理着资源(比如cpu、内存、文件等)。而将线程分配到某个cpu上执行。
- 默认一个进程有一个线程,为主线程。
- 线程的分类
- 线程按照其调度者可分为用户级线程和内核级线程两种:
- 用户级线程:主要解决的是上下文切换的问题,其调度过程由用户决定。
- 内核级线程:由内核调度机制实现。
- 现在大多数操作系统都采用用户级线程和内核级线程并存的方法。
- 用户级线程要绑定内核级线程运行,一个进程中的内核级线程会分配到固定的时间片,用户级线程分配的时间片以内核级线程为准。
- 默认情况下用户级线程和内核级线程是一对一,也可以多对一,但这样实时性就会比较差。
- 当cpu分配给线程的时间片用完后但线程没有执行完毕,此时线程会从运行状态返回到就绪状态,将cpu让给其他线程使用。
Linux线程的实现
- pthread线程库
- 以下线程均为用户级线程。在linux中,一般采用pthread线程库实现线程的访问与控制,由POSIX提出,具有良好的可移植性。
- Linux线程程序编译需要在gcc上链接库pthread。
- 线程的标识
- 每个进程内部的不同线程都有自己的唯一标识(ID)
- 线程标识只在它所属的进程环境中有效
- 线程标识是 pthread_t 数据类型
#include <pthread.h>
int pthread_equal(pthread_t , pthread_t );
返回:相等返回非0,否则返回0
pthread_t pthread_self(void);
返回:调用线程的线程ID
线程的创建
线程创建函数
- 使用pthread
- 使用pthread库需要包含pthread.h头文件
#include <pthread.h>
int pthread_creat(pthread_t* restrict tidp,
const pthread_attr_t* restrict attr,
void *(start_rtn)(void*),
void* restrict arg);
-
返回:成功返回0,否则返回错误编号。
-
参数
- tidp:线程标识符指针
- attr:线程属性指针,一般设为NULL
- start_rtn:线程运行函数的起始地址,是线程要运行的函数的指针
- arg:传递给线程运行函数的形式参数(指针本身也是数值,可以如此表达(void*)50 )
-
使用restrict(限制指针)关键字修饰的指针所指向的数据是唯一的,此数据仅能被该指针所使用,其他指针无法使用。
-
新创建线程从start_rtn函数的地址开始运行。
-
不能保证新线程和调用线程的执行顺序。
-
案例1
/*
* pthread_creat.c
* 龟兔赛跑
*/
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <math.h>
#include <unistd.h>
/*定义线程运行函数*/
void* th_fn(void *arg)
{
int distance = (int)arg;
for(int i=1; i<=distance; ++i)
{
printf("%lx run %d\\n",pthread_self(),i);
int time = (int)(drand48()*100000);
//drand48返回1~9的随机数,乘以10万,实现睡眠一个ms
usleep(time); //usleep单位微秒
}
return (void*)0;
}
int main(int argc, char **argv)
{
int err; //错误编码
pthread_t rabbit,turtle; //定义线程标识符
/*创建rabbit线程*/
/*创建成功返回非0编码,然后把线程的id放进rabbit中*/
if(0!=(err = pthread_create(&rabbit, NULL, th_fn, (void*)50 ))) //传给函数的形参50为兔子和乌龟行走的距离
{ perror("pthread_create error"); }
/*创建turtle线程*/
if(0!=(err = pthread_create(&turtle, NULL, th_fn, (void*)50 )))
{ perror("pthread_create error"); }
/*主控线程调用pthread_join(),调用者自己会阻塞
* 直到rabbit和turtle线程结束方可运行。*/
pthread_join(rabbit,NULL);
pthread_join(turtle,NULL);
//sleep(10); //让线程执行完毕再进入主控进程
printf("control thread ID: %lx\\n",pthread_self());
printf("finished!\\n");
return 0;
}
示例2 arg传递结构体
/*
* pthread_creat2.c
* 龟兔赛跑
*/
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <math.h>
#include <unistd.h>
typedef struct
{
char name[10];
int time;
int start; //起始点
int end; //结束点
}RaceArg;
void* th_fn(void *arg)
{
RaceArg *r = (RaceArg*)arg;
for(int i=r->start; i<=r->end; ++i)
{
printf("%s(%lx) running %d\\n",r->name,pthread_self(), i);
usleep(r->time);
}
return (void*)0;
}
int main(int argc, char **argv)
{
int err; //错误编码
pthread_t rabbit,turtle; //定义线程标识符
RaceArg r_a=
{"rabbit", (int)(drand48()*10000000),20,50};
RaceArg t_a=
{"turtle", (int)(drand48()*10000000),10,60};
if(0!=(err = pthread_create(&rabbit, NULL, th_fn, (void*)&r_a ))) //传入结构体指针
{ perror("pthread_create error"); }
if(0!=(err = pthread_create(&turtle, NULL, th_fn, (void*)&t_a )))
{ perror("pthread_create error"); }
pthread_join(rabbit,NULL);
pthread_join(turtle,NULL);
printf("control thread ID: %lx\\n",pthread_self());
printf("finished!\\n");
return 0;
}
进程的内存分配
- 线程各自的变量保存在线程各自的栈空间中,互相独立
- 数据段的全局变量为共享资源,线程都可以访问更改,但是为线程不安全,需要把他变成线程安全。
- 多线程编程中推荐使用局部变量。
线程的终止
线程终止的分类:
- 主动终止
- 线程的执行函数中调用return语句
- 调用pthread_exit()
- 被动终止
- 线程可以被同一进程的其他线程取消,其他线程调用pthread_cancel(pthid)
线程终止的函数
#include <pthread.h>
int pthread_cancel(pthread_t tid);
void pthread_exit(void *retval);
int pthread_join(pthread_t th, void **thread_return);
//返回值:成功返回0,否则返回错误编号
- pthread_cancel
- 线程可以被同一进程的其他线程取消,tid为被终止的线程标识符。
- pthread_exit
- retval: pthread_exit调用者线程的返回值,可由其他函数和pthread_join来检测获取。
- 线程退出时使用函数pthread_exit,是线程的主动行为。
- 由于一个进程中的多个线程共享数据段,因此通常在线程退出后,退出线程所占用的资源并不会随线程结束而释放。所以需要pthread_join函数来等待线程结束,类似于wait系统调用。
- pthread_join
- th:被等待线程的标识符。
- thread_return:用户定义指针,用来存储被等待线程的返回值。
/*
* pthread_term.c
*
*
*/
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
typedef struct
{
int d1;
int d2;
}Arg;
void* th_fn(void* arg)
{
Arg* r=(Arg*)arg;
//return (void*)(r->d1+r->d2); //输出是一个指针变量
return (void*)r; //输出一个结构体变量指针
}
int main(int argc, char **argv)
{
int err;
pthread_t th;
Arg r={20,50};
if(0!=(err = pthread_create(&th,NULL,th_fn,(void*)&r)))
perror("pthread_create error");
/*
int *result;
pthread_join(th, (void**)&result); //给th_fn返回的指针变量赋值
printf("result is %d\\n",(int)result);
*/
/*或者
int result;
pthread_join(th, (void*)&result);
printf("result is %d\\n",result);
*/
//当返回为结构体指针
int *result;
pthread_join(th, (void**)&result);
printf("result is %d\\n",\\
((Arg*)result)->d1+((Arg*)result)->d2);
return 0;
}
/*
* pthread_creat2.c
* 龟兔赛跑
*/
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <math.h>
#include <unistd.h>
typedef struct
{
char name[10];
int time;
int start; //起始点
int end; //结束点
}RaceArg;
void* th_fn(void *arg)
{
RaceArg *r = (RaceArg*)arg;
for(int i=r->start; i<=r->end; ++i)
{
printf("%s(%lx) running %d\\n",r->name,pthread_self(), i);
usleep(r->time);
}
//return (void*)0;//pthread_exit((void*)0);
return (void*)(r->end - r->start);
}
int main(int argc, char **argv)
{
int err; //错误编码
pthread_t rabbit,turtle; //定义线程标识符
RaceArg r_a=
{"rabbit", (int)(drand48()*10000000),20,50};
RaceArg t_a=
{"turtle", (int)(drand48()*10000000),10,60};
if(0!=(err = pthread_create(&rabbit, NULL, th_fn, (void*)&r_a ))) //传入结构体指针
{ perror("pthread_create error"); }
if(0!=(err = pthread_create(&turtle, NULL, th_fn, (void*)&t_a )))
{ perror("pthread_create error"); }
int result;
pthread_join(rabbit, (void*)&result);
printf("rabbit race distance is %d\\n", result);
pthread_join(turtle, (void*)&result);
printf("turtle race distance is %d\\n", result);
printf("race finished\\n");
/*
pthread_join(rabbit,NULL);
pthread_join(turtle,NULL);
*/
printf("control thread ID: %lx\\n",pthread_self());
printf("finished!\\n");
return 0;
}
输出:
turtle(b660c460) running 10
rabbit(b6e0d460) running 20
rabbit(b6e0d460) running 21
rabbit(b6e0d460) running 22
rabbit(b6e0d460) running 23
rabbit(b6e0d460) running 24
rabbit(b6e0d460) running 25
rabbit(b6e0d460) running 26
rabbit(b6e0d460) running 27
rabbit(b6e0d460) running 28
rabbit(b6e0d460) running 29
rabbit(b6e0d460) running 30
rabbit(b6e0d460) running 31
rabbit(b6e0d460) running 32
rabbit(b6e0d460) running 33
rabbit(b6e0d460) running 34
rabbit(b6e0d460) running 35
rabbit(b6e0d460) running 36
rabbit(b6e0d460) running 37
rabbit(b6e0d460) running 38
rabbit(b6e0d460) running 39
rabbit(b6e0d460) running 40
rabbit(b6e0d460) running 41
rabbit(b6e0d460) running 42
rabbit(b6e0d460) running 43
rabbit(b6e0d460) running 44
rabbit(b6e0d460) running 45
rabbit(b6e0d460) running 46
rabbit(b6e0d460) running 47
rabbit(b6e0d460) running 48
rabbit(b6e0d460) running 49
rabbit(b6e0d460) running 50
rabbit race distance is 30
turtle(b660c460) running 11
turtle(b660c460) running 12
turtle(b660c460) running 13
turtle(b660c460) running 14
turtle(b660c460) running 15
turtle(b660c460) running 16
turtle(b660c460) running 17
turtle(b660c460) running 18
turtle(b660c460) running 19
turtle(b660c460) running 20
turtle(b660c460) running 21
turtle(b660c460) running 22
turtle(b660c460) running 23
turtle(b660c460) running 24
turtle(b660c460) running 25
turtle(b660c460) running 26
turtle(b660c460) running 27
turtle(b660c460) running 28
turtle(b660c460) running 29
turtle(b660c460) running 30
turtle(b660c460) running 31
turtle(b660c460) running 32
turtle(b660c460) running 33
turtle(b660c460) running 34
turtle(b660c460) running 35
turtle(b660c460) running 36
turtle(b660c460) running 37
turtle(b660c460) running 38
turtle(b660c460) running 39
turtle(b660c460) running 40
turtle(b660c460) running 41
turtle(b660c460) running 42
turtle(b660c460) running 43
turtle(b660c460) running 44
turtle(b660c460) running 45
turtle(b660c460) running 46
turtle(b660c460) running 47
turtle(b660c460) running 48
turtle(b660c460) running 49
turtle(b660c460) running 50
turtle(b660c460) running 51
turtle(b660c460) running 52
turtle(b660c460) running 53
turtle(b660c460) running 54
turtle(b660c460) running 55
turtle(b660c460) running 56
turtle(b660c460) running 57
turtle(b660c460) running 58
turtle(b660c460) running 59
turtle(b660c460) running 60
turtle race distance is 50
race finished
control thread ID: b6fe10b0
finished!
线程的清理函数
类似于进程的终止函数atexit()。
include <pthread.h>
void pthread_clearnup_push( void (*rtn)(void*), void* arg);
void pthread_clearnup_pop(int execute);
以上一组代码是成对出现的,具体执行可写成:
while(execute)
{ //执行线程处理函数
}
- 参数
- rtn:清理函数的指针,清理函数也是自己定义的函数
- arg:调用清理函数传递的参数
- execute:值为1时执行线程清理函数,值为0时不执行线程清理函数
- 触发线程调用清理函数的动作
- 调用pthread_exit
- 响应取消请求,其他线程调用cancel
- 用非0 execute参数调用pthread_cleanup_pop时
#include <stdio.h>
#include <pthread.h>
void clean_fun(void *arg)
{
char* s=(char*)arg;
printf("clean_func: %s\\n",s);
}
void* th_fun(void* arg)
{
int execute = (int)arg; //execute为1时会调用清理函数
pthread_cleanup_push(clean_fun, "first clean func\\n");
pthread_cleanup_push(clean_fun, "second clean func\\n");
printf("thread running %lx\\n",pthread_self());
pthread_cleanup_pop(execute);
pthread_cleanup_pop(execute);
return (void*)0;
}
int main(void)
{
pthread_t th1,th2;
pthread_create(&th1,NULL,th_fun,(void*)1);
pthread_join(th1,NULL);
printf("th1(%lx) finished\\n",th1);
pthread_create(&th2,NULL,th_fun,(void*)1);
pthread_join(th2,NULL);
printf("th2(%lx) finished\\n",th1);
}
输出:
thread running b6d48460
/*栈操作,FILO*/
clean_func: second clean func
clean_func: first clean func
th1(b6d48460) finished
thread running b6d48460
clean_func: second clean func
clean_func: first clean func
th2(b6d48460) finished
进程、线程启动和终止方式的比较
进程 | 线程 |
---|---|
fork() | pthread_create() |
return/exit()/_exit() | return/pthread_exit() |
wait() | pthread_join() |
atexit() | pthread_clean_push()/pthread_clean_pop() |
线程的状态转换
新线程–pthread_create()–>就绪状态
就绪状态–获取CPU–>运行状态
运行状态–sleep()/pthread_join()–>阻塞状态
阻塞状态–sleep()结束/pthread_join()中断–>就绪状态
运行状态–运行结束或异常退出–>线程死亡
#线程属性
线程属性的初始化和销毁
#include <pthread.h>
int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);
返回:成功返回0,否则返回错误编号
- 线程属性结构
typedef struct
{
int detachstate; //线程的分离状态
int schedpolicy; //线程的调度策略
structsched_param schedparam; //线程的调度参数
int inheritsched; //线程的继承性
int scope; //线程的作用域
size_t guardsize; //线程栈的末尾的警戒缓冲区大小
int stackaddr_set; //线程的栈设置
void* stackaddr; //线程栈的位置
size_t stacksize; //线程栈的大小
}pthread_attr_t;
获得和设置分离属性
#include <pthread.h>
int pthread_attr_getdetachstate(const pthread_attr_t *restrict attr, int* detachstate); //获得
int pthread_attr_setdetachstate(const pthread_attr_t *attr, int detachstate); //设置
返回:成功返回0,否则返回错误编号
- detachstate取值
- PTHREAD_CREATE_JOINABLE(默认值) 正常启动线程
- PTHREAD_CREATE_DETACHED 以分离状态启动线程
- 以默认方式启动的线程,在线程结束后不会自动释放占有的系统资源,要在主控线程中调用pthread_join()后才会释放。
- 以分离状态启动的线程,在线程结束后会自动释放所占有的系统资源。
- 分离属性在网络通讯中使用的较多。
/*
* pthread_detach.c
*
*
*/
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void out_state(pthread_attr_t *attr)
{
int state;
pthread_attr_getdetachstate(attr,&state);
if(state==PTHREAD_CREATE_JOINABLE)printf("joinable state\\n");
if(state==PTHREAD_CREATE_DETACHED)printf("detached state\\n");
}
void* th_fn(void *arg)
{
int i;
int sum = 0;
for(i=1;i<=100;++i)sum+=i;
return (void*)sum;
}
int main(int argc, char **argv)
{
//int err;
pthread_t default_th, detach_th;
//定义线程属性
pthread_attr_t attr;
//对线程属性初始化
pthread_attr_init(&attr);
//输出分离属性
out_state(&attr);
//取分离属性默认值,以正常方式启动子线程
pthread_create(&default_th, &attr, th_fn, (void*)0);
int res;
pthread_join(default_th,(void*)&res);
printf("default return is %d\\n",(int)res);
printf("-----------------------\\n");
//设置分离属性为分离状态启动
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
out_state(&attr);
//以分离状态启动子线程,但是无法获得函数返回值
pthread_create<以上是关于Linux学习_线程的概念创建和终止的主要内容,如果未能解决你的问题,请参考以下文章
Linux_多线程(进程与线程的联系_pthread库_线程创建_线程等待_线程正常终止_线程取消_线程分离_pthread_t与LWP)