一、线程基础
进程:有独立的进程地址空间,有独立的pcb
线程:有独立的pcb,没有独立的进程地址空间
因此进程线程最本质的区别就是:是否共享地址空间
在Linux下线程是最小的执行单位;进程是最小的分配资源单位,可看成只有一个线程的进程
看进程下的线程号:ps -Lf 进程ID
LWP—>CPU执行的最小单位
线程共享和非共享
-
线程共享资源:
1,文件描述符表
2,每种信号的处理方式
3,当前工作目录
4,用户ID和组ID
5,内存地址空间(./text/.data/.bss/heap/共享库) -
线程非共享资源
1,线程id
2,处理器线程和栈指针
3,独立的栈空间(用户空间栈
4,errno变量
5,信号屏蔽字
6,调度优先级
gdb不支持线程,线程对信号支持也不太好
但线程可以提高程序并发性,且开销小,数据通信共享数据都方便
二、创建线程
线程控制原语
两个概念:
线程ID和线程号(LWP)
线程ID是在进程中用来表示线程身份的;线程号LWP是标识线程身份给cpu用的,用来划分时间轮线,决定每个程序的执行时间
1,pthread_create 创建一个新线程
第一个参数:传出新创建线程的传出ID;
第二个参数:描述线程的属性:通过调整线程属性可以调整线程各种状态,比如说线程分离,线程优先级,线程所占空间大小等,但是要是没有特殊要求就可以只传一个NULL空值;
第三个参数:一个回调参数
第四个参数:泛型指针,就是第三个参数所需要的参数,不需要参数就传入NULL
成功返回0,失败返回错误号
2,pthread_self 获取线程ID
pthread_t pthread_self(void);
注意线程ID是pthread_t类型的,且线程ID是进程内部的识别标识,两个进程间的线程ID是允许相同的
例子👇
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
void sys_err(const char*str)
{
perror(str);
exit(1);
}
void *tfn(void *argc)
{
printf("thread: pid = %d, tid = %lu\n", getpid(), pthread_self());
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t tid;
int ret = pthread_create(&tid, NULL, tfn, NULL);
if(ret != 0)
{
perror("pthread_create error");
}
printf("main: pid = %d, tid = %lu\n",getpid(),pthread_self());
sleep(1);
return 0;
}
可以看到主线程和子线程的进程id都是一样的,但是线程id是不同的
三、循环创建多个子线程
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
void sys_err(const char*str)
{
perror(str);
exit(1);
}
void *tfn(void *arg)
{
int i = (int)arg;
sleep(i);
printf("I am %dth thread: pid = %d, tid = %lu\n",i+1, getpid(), pthread_self());
return NULL;
}
int main(int argc, char *argv[])
{
int i;
int ret;//接收线程创建的返回值,用于确认创建是否成功
pthread_t tid;//第一个参数,传出参数,表示新创建线程的ID
for(i = 0; i<5; ++i)
{
ret = pthread_create(&tid,NULL,tfn,(void*)i)//往回调函数中传参数i,表示是第几个参数
if(ret != 0)
{
sys_err("pthread_create error");
}
}
sleep(i);//如果没有这一行很可能子线程回调函数还没来得及整个进程就没了
printf("main: pid = %d, tid = %lu\n",getpid(), pthread_self());
return 0;
}
还是一样的进程i,不同的线程id