Linux之多线程(一):线程介绍、创建和终止

115 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

线程概念

大条件:Linux中:

因为Linux环境下线程以进程的PCB模拟实现线程,所以LinuxPCB是线程。因此Linux线程也叫轻量级进程。

进程就是线程组。以前所描述的进程其实是单线程进程。

因为Linux线程是PCB,因此线程是CPU调度的基本单位。

因为进程是线程组,程序运行起来,资源是分配给整个线程组的,因此进程是资源分配的基本单位。

多进程可以并行多任务 / 多线程也可以并行多任务,何者更优?

多进程与多线程优缺点对比

  • 优点: 一个进程中的线程共用同一个虚拟地址空间,因此:
  1. 线程间通信更加方便。
  2. 线程的创建 / 销毁成本更低。
  3. 线程间切换调度成本更低。
  4. 线程的执行粒度更细致。
  • 缺点: 线程缺乏访问控制:系统调用(如exit()),异常针对的是整个进程,健壮性低。

多进程 / 多线程进行多任务处理的优势体现与细节:

  1. CPU密集型程序
  2. IO密集型程序

vfork()创建一个子进程共用同一块虚拟地址空间,避免出现调用栈混乱,因此子进程运行完毕或程序替换后父进程才开始运行。

多线程PCB使用同一块虚拟地址空间,如何实现同步运行而不会出现调用栈混乱?

为每个线程在虚拟地址空间中单独分配一块空间,每个线程都会有一些独立的信息(栈、寄存器、errno、信号屏蔽字、调度优先级) 线程之间共享的数据:代码段、数据段、文件描述符表、每种信号的处理方式、用户id、组id、当前工作目录路径。


线程控制

操作系统并没有为用户提供直接创建线程的系统调用接口,因此提供者封装了一套线程库,实现线程控制。 因此我们用户创建的线程是一个用户态线程,在内核态中对应了一个轻量级进程,实现程序的调度运行。

线程创建

pthread_create()接口

需要包含的头文件<pthread.h>,因为是库函数,因此链接时需要加上-pthread-lpthread选项来链接线程库。

int pthread_create(
	pthread_t *thread, 
	const pthread_attr_t *attr,
	void *(*start_routine) (void *),
	void *arg
);
  • thread:输出型参数,用于获取新创建的线程tid
  • attr:设置线程属性,通常置NULL
  • start_routine:线程的入口函数,是一个函数指针。
  • arg:作为实参,通过线程入口函数传递给线程。
  • 返回值: 成功返回0。失败返回errno,它的大小>0

区别:

  • tid:线程地址空间的首地址。
  • task_struct 中的pidLWP
  • task_struct 中的tgid:线程组id(主线程的pid)(也是进程id)。

ps -L:查看轻量级进程(线程)信息。其中LWP查看的就是task_struct中的pid

pthread_self()接口

返回调用线程的id

==线程创建实例==

void *thr_start(void *arg){
    //pthread_t pthread_self(void);
    //	返回调用线程id
    pthread_t tid = pthread_self();
    while(1) {
		printf("i am thread~~~%s-%p\n", (char*)arg, tid);
		sleep(1);
    }
    return NULL;
}
int main(int argc, char *argv[]){
    pthread_t tid;

    int ret = pthread_create(&tid, NULL, thr_start, (void*)"giturtle");
    if (ret != 0) {
		printf("thread create error\n");
		return -1;
    }
    printf("main tid:%p\n", pthread_self());
    printf("child tid:%p\n", tid);
    while(1) {
		printf("hello world~~~\n");
		sleep(1);
    }
    return 0;
}

运行结果

main tid:0x7ff449b99740
child tid:0x7ff4493a7700
hello world
i am thread~~nihao-0x7ff4493a7700
hello world
i am thread~~nihao-0x7ff4493a7700
hello world
i am thread~~nihao-0x7ff4493a7700
hello world
i am thread~~nihao-0x7ff4493a7700
hello world
i am thread~~nihao-0x7ff4493a7700
hello world
...

线程终止

  1. 在线程入口函数中returnmain函数中不能return,否则退出的是进程。

  2. 使用接口退出调用线程

pthread_exit()接口

pthread_exit(void *retval);

pthread_exit(hello!); 退出调用线程,主线程退出,进程并不会退出,等所有线程退出进程才会退出。 线程退出也会成为僵尸线程(普通线程退出无效果),线程也要保存返回值,线程地址空间无法被回收再利用,造成资源泄露。

  1. 使用接口取消一个指定的线程

pthread_cancel()接口

pthread_cancel(tid);

取消一个指定线程,方式为被动退出。

【注】如果一个线程是被取消的,则返回值是一个宏PTHREAD_CANCELED,它的值是-1

==线程终止实例==

void *thr_start(void *arg){
    while(1) {
		printf("child thread\n");
		sleep(1);
		//或者使用 pthread_exit(NULL);退出线程
    }
    return NULL;
}
int main(int argc, char *argv[]){
    pthread_t tid;

    int ret = pthread_create(&tid, NULL, thr_start, NULL);
    if (ret != 0) {
		printf("thread create error\n");
		return -1;
    }
    pthread_cancel(pthread_self());
    while(1) {
		printf("main thread\n");
		sleep(1);
    }
    return 0;
}

运行结果

main thread
child thread