当前位置: 首页 > news >正文

Linux 线程1-线程的概念、线程与进程区别、线程的创建、线程的调度机制、线程函数传参

目录

1.线程概念

1.1 线程的核心特点

1.2‌线程的工作模型‌

1‌.3线程的潜在问题‌ ‌

1.4 进程和线程区别

1.4.1‌执行与调度‌ ‌

1.4.2进程和线程区别对比表

1.4.3应用场景‌ ‌

1.4.4总结

2.线程的创建

2.1验证进程结束后,进程中所有的线程都会强制退出

2.2线程函数正常执行

3.线程调度机制

3.1调度策略与优先级 ‌

3.2不同调度策略的对比

3.3 调度触发机制 ‌

3.4 线程默认调度机制验证

4.线程函数传参


1.线程概念

线程(Thread)是操作系统能够进行调度的 最小执行单元,它是 进程内的一个独立执行流, 共享进程的资源(如内存、文件句柄等),但 拥有自己的栈空间和程序计数器。线程使得程序可以 同时处理多个任务,是并发编程的核心概念之一。

1.1 线程的核心特点

(1)轻量级
线程的创建、切换和销毁成本远低于进程,因为线程共享进程的地址空间和资源。 示例:启动一个线程只需分配私有栈(通常几MB),而创建进程需分配独立内存空间。
(2)共享资源
同一进程内的所有线程共享
  • 内存空间(堆、全局变量)
  • 打开的文件、网络连接等
  • 代码段(程序指令)
  • 环境变量 

线程间通信可直接通过共享内存,无需复杂的进程间通信(IPC)。

每个线程有独立的栈(用于存储局部变量和函数调用链)。
(3)并发执行
多个线程可以在单核CPU上通过时间片轮转“伪并行”,或在多核CPU上真正并行运行。
每个线程拥有独立的:
  • 线程ID(TID)
  • 程序计数器(PC)
  • 寄存器集合
  • 栈空间(用于保存局部变量、函数调用链) 

1.2‌线程的工作模型‌

‌单线程‌:传统程序按顺序执行,无法并行(如简单的脚本)。
‌多线程‌:程序拆分多个执行流,通过时间片轮转或并行执行提升效率。

1‌.3线程的潜在问题‌ ‌

(1)线程安全问题‌
多个线程同时修改共享数据时可能导致数据不一致。
需通过‌同步机制‌(互斥锁、信号量等)保护临界资源。
(2)调试困难‌
线程并发执行时,错误可能难以复现(如死锁、竞态条件)。
(3)资源限制‌
线程数量过多会导致:
  • 内存消耗增加(每个线程需独立栈空间)。
  • CPU频繁切换线程,降低效率。

1.4 进程和线程区别

1.4.1‌执行与调度‌ ‌

(1)进程‌: 操作系统以进程为单位分配CPU时间片,进程是‌资源分配的基本单位‌

 多进程程序可以充分利用多核CPU(并行执行)。 ‌

(2)线程‌: 线程是‌CPU调度的基本单位‌,同一进程的线程共享进程的时间片。

多线程程序在单核CPU上通过时间片切换实现“并发”,在多核CPU上可实现并行。

1.4.2进程和线程区别对比表

特性进程线程
资源独立性独立内存和资源共享进程内存和资源
创建/切换开销
通信成本高(需IPC)低(直接共享内存)
容错性高(崩溃不影响其他进程)低(线程崩溃可能影响整个进程)
适用场景隔离性要求高、任务独立高并发、数据共享需求高

1.4.3应用场景‌ ‌

(1)适合多进程的场景‌:

需要高隔离性(如浏览器、虚拟机)。

任务间无紧密协作,且需避免单点故障。

(2)适合多线程的场景‌:

需要高效共享数据(如Web服务器处理并发请求)。

计算密集型任务拆分到多核并行处理(如科学计算)。

1.4.4总结

一般把线程称之为轻量级的进程。一个进程可以创建多个线程,多个线程共享一个进程的资源。每一个进程创建的时候系统会给其4G虚拟内存,3G用户空间是私有的,所以进程切换 时,用户空间也会切换,所以会增加系统开销,而一个进程中的多个线程共享一个进程的资 源,所以线程切换时不用切换这些资源,效率会更高 。线程的调度机制跟进程是一样的,多个线程来回切换运行。

2.线程的创建

函数原型:

#include <pthread.h>

int pthread_create(pthread_t *thread,const pthread_attr_t *attr,
    void *(*start_routine)(void *),void *arg);

功能:
创建一个线程。

参数:
thread:线程标识符地址。
attr:线程属性结构体地址(默认 NULL)。
start_routine:线程函数的入口地址。
arg:传给线程函数的参数。

返回值:
成功:返回 0
失败:返回错误码(如 EAGAIN 表示资源不足,EINVAL 表示属性无效)‌

2.1验证进程结束后,进程中所有的线程都会强制退出

程序:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

//由于线程库原本不是系统本身的,所以在链接时需要手动链接库文件 gcc *.c -lpthread

void *thread_fun(void *arg)
{
    printf("子线程正在运行\n");
}

int main(int argc, char const *argv[])
{
    printf("主控线程正在执行\n");

    pthread_t thread;

    //通过pthread_create函数创建子线程
    if(pthread_create(&thread, NULL, thread_fun, NULL) != 0)
    {
        perror("fail to pthread_create");//创建失败
        exit(1);//退出
    }


    //由于进程结束后,进程中所有的线程都会强制退出,所以现阶段不要让进程退出
   // while(1);

    return 0;
}

运行结果:编译时加-lpthread, gcc 01_pthread.c -lpthread 

程序运行时,main函数执行完直接退出,线程也会退出,执行线程的函数未执行。

注意:
由于线程库原本不是系统本身的,所以在链接时需要手动链接库文件 gcc *.c -lpthread

2.2线程函数正常执行

程序:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

//由于线程库原本不是系统本身的,所以在链接时需要手动链接库文件 gcc *.c -lpthread

void *thread_fun(void *arg)
{
    printf("子线程正在运行\n");
}

int main(int argc, char const *argv[])
{
    printf("主控线程正在执行\n");

    pthread_t thread;

    //通过pthread_create函数创建子线程
    if(pthread_create(&thread, NULL, thread_fun, NULL) != 0)
    {
        perror("fail to pthread_create");//创建失败
        exit(1);//退出
    }

	//sleep(1); // 使用sleep() 也能保证线程函数执行。
	
    //由于进程结束后,进程中所有的线程都会强制退出,所以现阶段不要让进程退出
    while(1);

    return 0;
}
运行结果:

3.线程调度机制

3.1调度策略与优先级 ‌

(1)普通线程调度‌ ‌

默认策略‌:采用 ‌完全公平调度器(CFS)‌,基于时间片轮转和进程的 nice 值分配 CPU 时间‌。 ‌

调度目标‌:保证所有线程公平使用 CPU,运行时间与 nice 值成反比(nice 值范围:-20~19,默认 0)‌。 ‌

(2)实时线程调度‌ ‌

策略类型‌: ‌

SCHED_FIFO‌:先进先出,高优先级线程独占 CPU 直至主动释放或阻塞‌。 ‌

SCHED_RR‌:时间片轮转,高优先级线程运行固定时间片(默认 100ms)后被抢占‌。 ‌

优先级范围‌:实时线程优先级为 ‌1~99‌(1最低,99最高),始终优先于普通线程‌。

‌(3)优先级管理‌ 实时线程优先级通过 sched_setscheduler 设置,普通线程优先级通过 nice 调整‌。 调度器优先执行高优先级线程队列的头部线程,同优先级按策略(FIFO/RR)排序‌。

3.2不同调度策略的对比

调度策略调度机制是否依赖时间片上下文切换触发条件
CFS(普通线程)基于虚拟时间公平分配 CPUvruntime 差距过大、高优先级线程就绪
SCHED_RR固定时间片轮转是(如 100ms)时间片耗尽、更高优先级线程就绪
SCHED_FIFO先进先出,无时间片限制线程主动让出或更高优先级线程抢占
SCHED_DEADLINE优先执行截止时间(Deadline)最近的线程Deadline 到期或更高优先级线程就绪

3.3 调度触发机制 ‌

(1)调度时机‌ ‌

主动触发‌:系统调用返回、线程阻塞/唤醒、线程退出‌。 ‌

被动触发‌:时钟中断(检查时间片耗尽)、高优先级线程就绪‌。 ‌

抢占规则‌:高优先级线程可抢占低优先级线程,实时线程可抢占普通线程‌。

‌(2)时间片分配‌

实时线程时间片固定(如 RR 策略的 100ms),普通线程时间片由 CFS 动态计算(基于 nice 值和系统负载)‌。

3.4 线程默认调度机制验证

程序:

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

//一个进程中的多个线程执行顺序是不确定的,没有先后顺序可言
//多线程执行时跟进程一样,是来回切换运行的,跟进程的调度机制一样

void *pthread_fun1(void *arg)
{
    printf("子线程1开头 正在运行\n");
    sleep(1);
    printf("子线程1结尾 正在运行\n");
}

void *pthread_fun2(void *arg)
{
    printf("子线程2开头 正在运行\n");
    sleep(1);
    printf("子线程2结尾 正在运行\n");
}

void *pthread_fun3(void *arg)
{
    printf("子线程3开头 正在运行\n");
    sleep(1);
    printf("子线程3结尾 正在运行\n");
}

int main(int argc, char const *argv[])
{
    pthread_t thread1, thread2, thread3;

    if(pthread_create(&thread1, NULL, pthread_fun1, NULL) != 0)
    {
        perror("fail to pthread_create thread1");
    }

    if(pthread_create(&thread2, NULL, pthread_fun2, NULL) != 0)
    {
        perror("fail to pthread_create thread2");
    }

    if(pthread_create(&thread3, NULL, pthread_fun3, NULL) != 0)
    {
        perror("fail to pthread_create thread3");
    }

    while(1);

    return 0;
}

运行结果:

一个进程中的多个线程执行顺序是不确定的,多线程执行时跟进程一样,是来回切换运行的,跟进程的调度机制一样。

4.线程函数传参

程序:

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

int num = 100;//全局变量,线程共享内存

//线程处理函数可以认为就是一个普通的全局函数,只不过与普通函数最大的区别
//在于,线程处理函数是并行执行,来回交替执行,但是普通函数一定是按照顺序一个一个执行
void *pthread_fun1(void *arg)
{
    printf("子线程1:num = %d\n", num);
    num++;//修改全局变量,其他线程获取到的是修改后的值

    int n = *(int *)arg;// arg强转为 int *

    printf("main函数局部变量a修改前,在子线程1为: %d\n", n);
    *(int *)arg = 111;//修改局部变量 a ,其他线程获取到的是修改后的值
    n = *(int *)arg;
     printf("main函数局部变量a修改后,在子线程1为: %d\n", n);
}

void *pthread_fun2(void *arg)
{
    sleep(1);//保证 pthread_fun1 先执行完
    printf("子线程2:num = %d\n", num);

    int n = *(int *)arg;
    printf("main函数局部变量a,在子线程2为: %d\n", n);
}

int main(int argc, char const *argv[])
{
    pthread_t thread1, thread2;

    int a = 666;

    if(pthread_create(&thread1, NULL, pthread_fun1, (void *)&a) != 0)
    {
        perror("fail to pthread_create");
    }

    if(pthread_create(&thread2, NULL, pthread_fun2, (void *)&a) != 0)
    {
        perror("fail to pthread_create");
    }

    while(1);

    return 0;
}

运行结果:全局变量、局部变量在线程1修改后,执行线程2获取到的变量为修改后变量。

http://www.dtcms.com/a/111201.html

相关文章:

  • SpringBoot+Spring+MyBatis相关知识点
  • MQL5教程 05 指标开发实战:双色线、双线变色MACD、跨时间周期均线
  • TSMaster在新能源汽车研发测试中的硬核应用指南
  • 【rockchip】使用RKMPP+RGA解码H264并转换数据格式输出
  • 一文理解什么是中值模糊
  • C++多线程函数介绍
  • 【Kafka基础】ZooKeeper在Kafka中的核心作用:分布式系统中枢神经系统
  • 【LeetCode Solutions】LeetCode 141 ~ 145 题解
  • RocketMQ 中的 ProducerManager 组件剖析
  • 【Java Stream详解】
  • 提高:图论:强连通分量 图的遍历
  • Nginx功能及应用全解:从负载均衡到反向代理的全面剖析
  • OpenAI:人工智能领域的探索者与变革者
  • 黑马点评redis改 part 1
  • T-SQL语言的链表查找
  • eventEmitter实现
  • 网络建设与运维神州数码DCN MAC地址表操作
  • TypedDict和dataclass的优缺点对比
  • 前馈控制与反馈控制融合算法详解及python案例分析
  • JavaWeb学习--MyBatis-Plus整合SpringBoot的ServiceImpl方法(增加,修改与删除部分)
  • 深入解析:使用Python爬取Bilibili视频
  • 如何用DeepSeek进行SWOT分析?以CSDN的“C知道”为例
  • k8s的StorageClass存储类和pv、pvc、provisioner、物理存储的链路
  • 做一个Andriod系统应用的方法
  • 软件设计师之设计模式
  • 第七章 Python基础进阶-异常、模块与包(其五)
  • 手撕AVL树
  • 模运算核心性质与算法应用:从数学原理到编程实践
  • Julia语言的测试覆盖率
  • 卷积神经网络CNN 经典模型 — GoogleLeNet、ResNet、DenseNet算法原理与模型构造