Linux下创建所线程

一. 线程

线程是轻量级的进程(LWP:light weight process),在 Linux 环境下线程的本质仍是进程。在计算机上运行的程序是一组指令及指令参数的组合,指令按照既定的逻辑控制计算机运行。操作系统会以进程为单位,分配系统资源,可以这样理解,进程是资源分配的最小单位,线程是操作系统调度执行的最小单位。

1.进程有自己独立的地址空间,多个线程共用同一个地址空间

线程更加节省系统资源,效率不仅可以保持的,而且能够更高
在一个地址空间中多个线程独享:每个线程都有属于自己的栈区,寄存器 (内核中管理的)
在一个地址空间中多个线程共享:代码段,堆区,全局数据区,打开的文件 (文件描述符表) 都是线程共享的

2.线程是程序的最小执行单位,进程是操作系统中最小的资源分配单位

每个进程对应一个虚拟地址空间,一个进程只能抢一个 CPU 时间片
一个地址空间中可以划分出多个线程,在有效的资源基础上,能够抢更多的 CPU 时间片

3.CPU 的调度和切换:线程的上下文切换比进程要快的多

 上下文切换:进程 / 线程分时复用 CPU 时间片,在切换之前会将上一个任务的状态进行保存,下次切换回这个任务的时候,加载这个状态继续运行,任务从保存到再次加载这个过程就是一次上下文切换。
线程更加廉价,启动速度更快,退出也快,对系统资源的冲击小。

4.在处理多任务程序的时候使用多线程比使用多进程要更有优势,但是线程并不是越多越好,如何控制线程的个数呢?

文件 IO 操作:文件 IO 对 CPU 是使用率不高,因此可以分时复用 CPU 时间片,线程的个数 = 2 * CPU 核心数 (效率最高)
处理复杂的算法 (主要是 CPU 进行运算,压力大),线程的个数 = CPU 的核心数 (效率最高)

二. 创建线程

每一个线程都有一个唯一的线程 ID,ID 类型为 pthread_t,这个 ID 是一个无符号长整形数,如果想要得到当前线程的线程 ID,可以调用如下函数:

pthread_t pthread_self(void);	// 返回当前线程的线程ID

在一个进程中调用线程创建函数,就可得到一个子线程,和进程不同,需要给每一个创建出的线程指定一个处理函数,否则这个线程无法工作。

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine) (void *), void *arg);
// Compile and link with -pthread, 线程库的名字叫pthread, 全名: libpthread.so libptread.a

参数:

thread: 传出参数,是无符号长整形数,线程创建成功,会将线程 ID 写入到这个指针指向的内存中

attr: 线程的属性,一般情况下使用默认属性即可,写 NULL

start_routine: 函数指针,创建出的子线程的处理动作,也就是该函数在子线程中执行。

arg: 作为实参传递到 start_routine 指针指向的函数内部
在这里插入代码片:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>

 void  callback(void* arg)
{
    for(int i = 0; i < 5; i++  )
    {
         printf("子线程 : i = %ld\n",i);
    }  
    printf("子线程 : %ld\n",pthread_self());
    return NULL;
}

int main()
{
    pthread_t  tid;
    pthread_create(&tid ,  NULL , callback , NULL);    //callback 是一个回调函数
    for(int i = 0; i < 5; i++  )
    {
         printf("主线程 : i = %ld\n",i);
    }  
    printf("主线程 : %ld\n",pthread_self());
    sleep(3);    //睡眠3s
    return 0;
}

在这里插入图片描述

编译测试程序,会看到如下错误信息:

在这里插入代码片
$ gcc pthread_create.c 
/tmp/cctkubA6.o: In function `main':
pthread_create.c:(.text+0x7f): undefined reference to `pthread_create'
collect2: error: ld returned 1 exit status

错误原因是因为编译器链接不到线程库文件(动态库),需要在编译的时候通过参数指定出来,动态库名为 libpthread.so 需要使用的参数为 -l,根据规则掐头去尾最终形态应该写成:-lpthread(参数和参数值中间可以有空格)。正确的编译命令为:

# pthread_create 函数的定义在某一个库中, 编译的时候需要加库名 pthread
$ gcc pp.c -lpthread -o app
$ ./app
子线程创建成功, 线程ID: 140310795798272
我是主线程, 线程ID: 140310804322112
i = 0
i = 1
i = 2
............

在打印的日志输出中为什么子线程处理函数没有执行完毕呢(只看到了子线程的部分日志输出)?
主线程一直在运行,执行期间创建出了子线程,说明主线程有 CPU 时间片,在这个时间片内将代码执行完毕了,主线程就退出了。子线程被创建出来之后需要抢cpu时间片, 抢不到就不能运行,如果主线程退出了, 虚拟地址空间就被释放了, 子线程就一并被销毁了。但是如果某一个子线程退出了, 主线程仍在运行, 虚拟地址空间依旧存在。

得到的结论:在没有人为干预的情况下,虚拟地址空间的生命周期和主线程是一样的,与子线程无关。

目前的解决方案:让子线程执行完毕,主线程再退出,可以在主线程中添加挂起函数 sleep();



本文转载作者:
作者: 苏丙榅
链接: https://subingwen.cn/linux/thread/#2-2-%E5%88%9B%E5%BB%BA%E7%BA%BF%E7%A8%8B

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值