linux 线程

文章目录

1.线程概念

进程=内核数据结构 + 进程代码和数据
我们的代码在进程中,全部都是串行调用的
进程创建,成本较高(时间和空间),不仅需要创建PCB,页表等,还需要进行初始化,时间和空间成本高,所以多进程的目的是为了多执行流并发执行
地址空间和地址空间上的虚拟地址,本质是一种资源,一个进程的大部分资源都可以通过地址空间来访问
在内核角度,进程是承担分配系统资源的基本实体
当一个程序被执行时,操作系统会为其创建一个进程,并为该进程分配必要的系统资源,如内存空间、设备I/O等。这些资源是进程执行程序所必需的,只有被分配给进程后,才能被进程所使用。


线程是进程内部的一个执行分支,是CPU调度的基本单位,线程在进程地址空间内运行
Linux设计者认为,进程和线程都是执行流,具有极度的相似性,没必要单独设计数据结构和算法,直接复用代码,用进程模拟线程,也就是说,Linux中没有真正的线程
CPU调度时不需要区分进程和线程,都是执行流,在Linux中,所有的调度执行流都叫做轻量级进程(LWP),OS在调度时,使用LWP来进行调度
可以用是 ps -aL 查看轻量级进程

线程优点

创建一个新线程的代价比创建一个新进程的代价小得多
与进程切换相比,线程之间的切换需要OS做的工作要少很多---CPU内存在cache,切换进程,需要将CPU内的硬件级cache数据丢弃,重新填充
线程占用的资源比进程要少很多

线程缺点

健壮性降低:健壮性主要指的是线程在并发执行过程中,面对各种问题时,能够保持其正常功能和性能的能力,编写多线程程序需要更深入的考虑
缺乏访问控制:进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响。
编程难度提高:编写与调试一个多线程程序比单线程程序困难得多

2.Linux线程和进程

  • 进程是承担分配系统资源的基本实体
  • 线程是CPU调度的基本单位
  • 线程私有的数据:
    调度:CPU运行的上下文数据
    常规运行:独立的栈结构
  • 线程在进程地址空间内运行,在同一进程的同一地址空间中,所有线程的数据区(已初始化数据区和未初始化数据区)和代码区都是共享的,定义的全局变量每个线程都可以访问到

3. Linux线程控制

  • Linux中没有真正的线程,只有轻量级进程,所以Linux系统不会有线程相关的系统调用,只有轻量级进程的系统调用
  • 为了用户更好使用,有了pthread库(原生线程库)),将轻量级进程系统调用进行封装,转换为线程相关接口提供给用户。不属于内核,用户级
  • 要使用这些接口,需要包含头文件<pthread.h>,同时编译时指定库 -lpthread

线程创建---pthread_create

thread:输出型参数,返回线程ID

attr:设置线程的属性,默认设置为NULL即可

start_routine:参数为void*,返回值为void*的函数指针,是线程启动后执行的函数

arg:传给线程启动函数的参数

pthreads函数成功返回0,失败返回错误码

cpp 复制代码
#include <iostream>
#include <unistd.h>
#include <pthread.h>


void* threadrun(void*)
{
    while(true)
    {
        std::cout << "I am new thread" << std::endl;
        sleep(1);
    }
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, threadrun, nullptr);
    while(true)
    {
        std::cout << "I am main thread" << std::endl;
        sleep(1);
    }
    
    return 0;
}
  • 获取线程ID pthread_self
  • 获取线程ID: 1.创建线程时输出型参数获取 2.pthread_self
cpp 复制代码
#include <iostream>
#include <string>
#include <unistd.h>
#include <pthread.h>

std::string ToHEX(pthread_t tid)
{
    char buff[128];
    snprintf(buff, sizeof(buff) - 1, "%#lx", tid);
    return buff;
}

void* threadrun(void*)
{
    while(true)
    {
        std::cout << "I am new thread, tid :" << ToHEX(pthread_self()) << std::endl;
        sleep(1);
    }
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, threadrun, nullptr);
    while(true)
    {
        std::cout << "I am main thread, new thread tid:" << ToHEX(tid) << std::endl;
        sleep(1);
    }

    return 0;
}

线程等待---pthread_join

线程被创建,也需要被等待,不等待会有内存泄露的问题,同时也需要获取线程的退出信息

retval:输出型参数,为线程函数的返回值

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include <pthread.h>

std::string ToHEX(pthread_t tid)
{
    char buff[128];
    snprintf(buff, sizeof(buff) - 1, "%#lx", tid);
    return buff;
}

struct Thread
{
    Thread(pthread_t tid, const std::string& name)
        :_tid(tid), _threadName(name)
    {}
    pthread_t _tid;
    std::string _threadName;
};

void* threadrun(void* args)
{
    std::string* name = static_cast<std::string*>(args);
    int cnt = 10;
    while(cnt--)
    {
        std::cout << *name << " is running...." << std::endl;
        sleep(1);
    }
    return (void*)100;
}

int main()
{
    const int threadNum = 5;
    std::vector<Thread> threads;
    for (int i = 0; i < threadNum; i++)
    {
        pthread_t tid;
        std::string* name = new std::string("thread-" + std::to_string(i + 1));
        pthread_create(&tid, nullptr, threadrun, name);
        threads.emplace_back(tid, *name);
    }
    void* ret;
    for (auto& thread : threads)
    {
        pthread_join(thread._tid, &ret);
        std::cout << thread._threadName << "is quit, return val: " << (long long int)ret << std::endl;
    }
    return 0;
}

线程退出

    1. 从线程函数直接return
    1. pthread_exit

      retval:退出信息
cpp 复制代码
#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include <pthread.h>

void* threadrun(void*)
{
    int cnt = 5;
    while(cnt--)
    {
        std::cout << "线程还有" << cnt << "秒退出" << std::endl;
        sleep(1);
    }
    std::string* ret = new std::string("11111111111111111111111111");
    pthread_exit(ret);
}


int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, threadrun, nullptr);

    void* ret;
    pthread_join(tid, &ret);
    std::string* retval = static_cast<std::string*>(ret);
    std::cout << "thread return val: " << *retval << std::endl;

    return 0;
}

线程调用pthread_exit()终止,pthread_join()所获得的返回值为pthread_exit()的参数

    1. 一个线程调用pthread_cancel终止同一进程中的另一个线程保证新线程已经启动的前提下
cpp 复制代码
#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include <pthread.h>

void* threadrun(void*)
{
    while(true)
    {
        std::cout << "new thread running...." << std::endl;
        sleep(1);
    }
}


int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, threadrun, nullptr);

    sleep(2);
    pthread_cancel(tid);

     void* ret;
     pthread_join(tid, &ret);
    
     std::cout << "thread return val: " << (long long int)ret << std::endl;

    return 0;
}

线程被取消,退出信息是-1,-1是一个宏

线程分离---pthread_detach

  • 默认情况下,线程是需要被等待的,线程退出后,要对其进行pthread_join操作,否则无法释放资源,造成资源泄露
  • 如果不关心线程返回值,可以分离线程,线程退出时,自动释放资源
    分离是线程的一种工作状态,底层依旧是同一个进程,只是不需要且禁止join了
相关推荐
wdxylb29 分钟前
云原生俱乐部-shell知识点归纳(1)
linux·云原生
飞雪20072 小时前
Alibaba Cloud Linux 3 在 Apple M 芯片 Mac 的 VMware Fusion 上部署的完整密码重置教程(二)
linux·macos·阿里云·vmware·虚拟机·aliyun·alibaba cloud
路溪非溪2 小时前
关于Linux内核中头文件问题相关总结
linux
Lovyk4 小时前
Linux 正则表达式
linux·运维
Fireworkitte5 小时前
Ubuntu、CentOS、AlmaLinux 9.5的 rc.local实现 开机启动
linux·ubuntu·centos
sword devil9006 小时前
ubuntu常见问题汇总
linux·ubuntu
ac.char6 小时前
在CentOS系统中查询已删除但仍占用磁盘空间的文件
linux·运维·centos
淮北也生橘127 小时前
Linux的ALSA音频框架学习笔记
linux·笔记·学习
华强笔记11 小时前
Linux内存管理系统性总结
linux·运维·网络
十五年专注C++开发11 小时前
CMake进阶: CMake Modules---简化CMake配置的利器
linux·c++·windows·cmake·自动化构建