【linux学习】多线程(1)

文章目录

  • 线程的概念
    • 线程与进程
  • 线程的用法
    • 线程的创建
      • 多线程
    • 线程的等待
    • 线程锁
      • 死锁

线程的概念

在Linux中,线程(Thread)是程序执行流的最小单位,是进程中的一个实体,负责在程序中执行代码。线程本身不拥有系统资源,但它可以访问其所属进程的资源,包括内存空间、文件句柄等。线程与进程的主要区别在于线程是共享进程资源的,而进程之间则是独立的。

线程与进程

在Linux中,线程有时被称为轻量级进程(Lightweight Process, LWP)。从内核的角度来看,线程和进程在很大程度上是相似的,它们都通过task_struct结构体来描述。但是,线程与父进程(或其他线程)共享某些资源,如地址空间和文件描述符表,而进程则拥有自己的独立资源。

在这里插入图片描述
PCB(Process Control Block)即进程控制块。
虽然线程和进程都使用task_struct来描述,但Linux内核通过一些特定的字段来区分它们。例如,每个task_struct都有一个pid(进程ID)和一个tgid(线程组ID)。对于主线程(也就是我们通常所说的“进程”),其pid和tgid是相同的。但是,对于该进程创建的其他线程,它们的pid是唯一的,但tgid与主线程的tgid(也就是该进程的ID)相同。通过这种方式,内核可以区分一个task_struct描述的是进程还是线程。
进程与线程关系图:
(波浪线代表一个线程)
在这里插入图片描述

线程的用法

线程的创建

需要pthread_creart函数来创建线程。
在这里插入图片描述

#include <iostream>
#include <unistd.h>
#include <pthread.h>
void *ThreadRoutine(void *args)
{
    const char *threadname = (const char *)args;
    while (true)
    {
        std::cout << "i am a new: " << threadname << std::endl;
        sleep(1);
    }
}
int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, ThreadRoutine, (void *)"thread 1");
    while(true)
    {
        std:: cout << "i am main thread" << std::endl;
        sleep(1);
    }
    return 0;
}

在这里插入图片描述
看图可以得知我们创建了一个线程,主线程仍然在继续运行。

多线程

void *ThreadRoutine(void *args)
{
    const char *threadname = (const char *)args;
    while (true)
    {
        std::cout << "i am a new: " << threadname << std::endl;
        sleep(3);
    }
}
int main()
{
    pthread_t tid1;
    pthread_create(&tid1, nullptr, ThreadRoutine, (void *)"thread 1");
    sleep(3);

    pthread_t tid2;
    pthread_create(&tid2, nullptr, ThreadRoutine, (void *)"thread 2");
    sleep(3);

    pthread_t tid3;
    pthread_create(&tid3, nullptr, ThreadRoutine, (void *)"thread 3");
    sleep(3);

    pthread_t tid4;
    pthread_create(&tid4, nullptr, ThreadRoutine, (void *)"thread 4");
    sleep(3);

    while (true)
    {
        std::cout << "i am main thread" << std::endl;
        sleep(3);
    }
    return 0;
}

可以看出线程2,3,4都正常运行。
在这里插入图片描述

线程的等待

在这里插入图片描述
pthread_t thread:这是你想要等待的线程的标识符。这个标识符是在调用pthread_create时返回的。
**void retval:这是一个指向指针的指针,用于获取被等待线程的返回值。如果retval不是NULL,那么pthread_join会将终止线程的返回值放在*retval所指向的位置。如果你对线程的返回值不感兴趣,可以将这个参数设置为NULL。

#include <iostream>
#include <unistd.h>
#include <pthread.h>
void *ThreadRoutine(void *args)
{
    int cnt = 5;
    const char *threadname = (const char *)args;
    while (cnt)
    {
        std::cout << "i am a new: " << threadname << std::endl;
        sleep(1);
        cnt--;
    }
    return args;
}
int main()
{
    pthread_t tid1;
    pthread_create(&tid1, nullptr, ThreadRoutine, (void *)"thread 1");
    void *ret = nullptr;
    pthread_join(tid1,&ret);
    std:: cout << (char *)ret << std::endl;
    sleep(3);
    //while ()
    //{
        std::cout << "i am main thread" << std::endl;
        sleep(1);
    //}
    return 0;
}

在这里插入图片描述
在函数体内,我们将args作为返回值,可以看到将函数的返回值通过pthread_join()函数传送到ret这个指针上.

线程锁

线程锁(Thread Lock)是一种同步机制,主要用于解决多线程访问共享资源时可能出现的并发问题。

#include <iostream>
#include <unistd.h>
#include <pthread.h>
int ticket = 10000;
void *ThreadRoutine(void *args)
{
    const char *threadname = (const char *)args;
    while (ticket > 0)
    {
        std::cout << "i am : " << threadname << "getticket:" << ticket-- << std::endl;
    }
    return args;
}
int main()
{
    pthread_t tid1;
    pthread_create(&tid1, nullptr, ThreadRoutine, (void *)"thread 1");
    pthread_t tid2;
    pthread_create(&tid2, nullptr, ThreadRoutine, (void *)"thread 2");
    pthread_t tid3;
    pthread_create(&tid3, nullptr, ThreadRoutine, (void *)"thread 3");
    pthread_t tid4;
    pthread_create(&tid4, nullptr, ThreadRoutine, (void *)"thread 4");
    void *ret1 = nullptr;
    void *ret2 = nullptr;
    void *ret3 = nullptr;
    void *ret4 = nullptr;

    pthread_join(tid1, &ret1);
    pthread_join(tid2, &ret2);
    pthread_join(tid3, &ret3);
    pthread_join(tid4, &ret4);
    std::cout << "i am main thread,Finish" << std::endl;
    return 0;
}

如以上代码,我们模拟一个多个线程抢车票的的进程。代码中设计当车票为零时,则退出while循环,并且退出函数,退出线程。
但经过我们多次试验发现图下现象:
在这里插入图片描述
票数竟会变成负数?
假设ticket值为1当一个线程已经进入while循环内,但对于ticket值并没有做出改变,此时另一个线程就会用相同的ticket值也进入了while循环,两个进程又都进行了减减操作,导致ticket值变为-1.
这时就要用上锁,来保证多线程防止同时访问共享资源。
代码实现:

#include <iostream>
#include <unistd.h>
#include <pthread.h>
int ticket = 10000;
pthread_mutex_t lock;//全局锁
void *ThreadRoutine(void *args)
{
    const char *threadname = (const char *)args;
    pthread_mutex_lock(&lock);//上锁
    while (ticket > 0)
    {
        std::cout << "i am : " << threadname << "getticket:" << ticket-- << std::endl;
    }
    pthread_mutex_unlock(&lock);//解锁
    return args;
}
int main()
{
    pthread_mutex_init(&lock, nullptr);

    pthread_t tid1;
    pthread_create(&tid1, nullptr, ThreadRoutine, (void *)"thread 1");
    pthread_t tid2;
    pthread_create(&tid2, nullptr, ThreadRoutine, (void *)"thread 2");
    pthread_t tid3;
    pthread_create(&tid3, nullptr, ThreadRoutine, (void *)"thread 3");
    pthread_t tid4;
    pthread_create(&tid4, nullptr, ThreadRoutine, (void *)"thread 4");
    void *ret1 = nullptr;
    void *ret2 = nullptr;
    void *ret3 = nullptr;
    void *ret4 = nullptr;

    pthread_join(tid1, &ret1);
    pthread_join(tid2, &ret2);
    pthread_join(tid3, &ret3);
    pthread_join(tid4, &ret4);
    std::cout << "i am main thread,Finish" << std::endl;
    return 0;
}

通过给区间上锁,来防止多线程同时访问资源。

死锁

在这里插入图片描述
如上图,线程A,线程B,倘若线程A中申请到了LOCK1,而线程B中申请到了LOCK2,线程A等待LOCK2的释放,线程B等待LOCK1的释放,就会导致互相等待,两个线程都进行不下去,导致死锁
如何避免:

避免嵌套锁:尽量不在持有锁的同时请求另一个锁。如果必须这样做,确保加锁的顺序在所有线程中都是一致的。
保持锁的顺序一致:多个线程在尝试获取多个锁时,总是以相同的顺序请求它们。这可以防止循环等待条件的发生,即线程A等待线程B释放锁,而线程B又在等待线程A释放另一个锁。
使用超时机制:在尝试获取锁时设置超时。如果线程不能在规定的时间内获得锁,它将放弃并稍后重试。这可以防止线程无限期地等待,从而增加了系统的灵活性。
减少锁的粒度:尽量只锁定需要保护的最小资源范围。例如,如果你可以只锁定一个数据结构的一部分而不是整个数据结构,那么这将减少死锁的可能性。
避免长时间持有锁:尽量缩短持有锁的时间。这意味着你应该在获得锁后尽快完成你的工作并释放锁。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/619946.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

flink尚硅谷

flink 1 flink基础使用1.1 角色1.2 部署模式&#xff08;抽象&#xff09;1.2.1 会话模式1.2.2 单作业模式1.2.3 应用模式 1.3 运行模式&#xff08;实际 谁来管理资源&#xff09;1.3.1 Stand alone1.3.2 YARN运行模式&#xff08;重点&#xff09; 2. 运行时架构2.1 系统架构…

windows 10安装 docker desktop

升级 windows 10 windows 10 升级到 20H2&#xff0c;如 20H2 19045.4291。 注意&#xff1a;需返回更新&#xff0c;重启计算机&#xff0c;确保更新完整。 bios 开启虚拟化 开启cpu虚拟化功能。 windows 启用功能 启用hyper-v 启用 wsl 安装 wsl https://learn.microso…

锁策略详解:互斥锁、读写锁、乐观锁与悲观锁、轻量级锁与重量级锁、自旋锁、偏向锁、可重入锁与不可重入锁、公平锁与非公平锁

一.锁策略 锁策略指的是在多线程编程中用于管理共享资源访问的规则和技术。它们确保在任何给定时间只有一个线程可以访问共享资源&#xff0c;以防止竞态条件和数据不一致性问题。常见的锁策略包括&#xff1a; 互斥锁&#xff08;Mutex&#xff09;&#xff1a;最常见的锁类型…

导航app为什么知道还有几秒变绿灯?

在使用地图导航app行驶至信号灯的交叉路口时&#xff0c;这些应用程序会贴心地告知用户距信号灯变化还有多少秒&#xff0c;无论是即将转为绿灯还是红灯。这一智能化提示不仅使得驾驶员能适时做好起步或刹车的准备&#xff0c;有效缓解了因等待时间不确定而产生的焦虑情绪&…

活动预告|“AI+Security”系列第1期:大模型网络空间安全前沿探索活动火热报名中

由Wisemodel社区、安全极客主办的 “AISecurity”系列第1期&#xff1a; 大模型网络空间安全前沿探索 线下活动 将于2024年5月18日下午14:00 在苏州街16号神州数码大厦5层举行 本活动旨在汇聚业界专家和实践者共同探讨和推进AI自身安全、AI赋能安全与AI给安全带来的挑战等关…

Blender动画与云渲染:创造高质量作品的未来路径

Blender作为开源的3D图形软件&#xff0c;在多个领域广受欢迎。但随着项目复杂度提升&#xff0c;传统渲染方式受限。云渲染技术的兴起突破了这些限制&#xff0c;为创作者提供了更自由、高效的创作环境。 一、Blender动画项目的挑战 传统上&#xff0c;Blender动画渲染需要依…

【C语言】深度解析:动态内存管理的机制与实践

&#x1f525;引言 本篇将深度解析:动态内存管理的机制。为了更加灵活分配内存中的空间&#xff0c;库中为了我们提供了一些的函数&#xff0c;去动态开辟和释放堆上的空间。 &#x1f308;个人主页&#xff1a;是店小二呀 &#x1f308;C语言笔记专栏&#xff1a;C语言笔记 &a…

【Redis】Redis入门概述

&#x1f600;大家好&#xff0c;我是白晨&#xff0c;一个不是很能熬夜&#x1f62b;&#xff0c;但是也想日更的人✈。如果喜欢这篇文章&#xff0c;点个赞&#x1f44d;&#xff0c;关注一下&#x1f440;白晨吧&#xff01;你的支持就是我最大的动力&#xff01;&#x1f4…

选择数据摆渡系统 哪个厂家比较好?

数据摆渡系统是一种用于在不同网络或安全域之间安全传输数据的技术解决方案。它通常用于解决网络隔离环境下的数据交换问题&#xff0c;确保数据在传输过程中的安全性、完整性和合规性。数据摆渡系统可以采用物理手段&#xff0c;如通过专用隔离硬件和私有安全协议&#xff0c;…

数据结构的二叉树(c语言版)

一.二叉树的概念 1.二叉树的基本概念 二叉树是一种常见的树状数据结构&#xff0c;它由若干个节点组成&#xff0c;这些节点通过边连接起来。每个节点最多可以有两个子节点&#xff0c;分别称为左子节点和右子节点。 二叉树的特点是每个节点最多有两个子节点&#xff0c;而且…

上海市青少年算法2023年12月月赛(丙组)试题解析

上海市青少年算法2023年12月月赛(丙组)试题解析 T1数砖数 题目描述 给定一种 22 规格的瓷砖,该瓷砖的式样为 ## .# 用这种瓷砖,从平面的左上角出发,将整个平面铺满,形如: 给定两个整数 n 与 m,请计算从左上角开始的 n 行 m 列的区域中,有多少格子是 #。 输入格式 第一…

易百纳与成都鼎桥达成战略合作,共创海鸥派生态新篇章

前言 易百纳技术社区&#xff08;以下简称“易百纳”&#xff09;与成都鼎桥通信技术有限公司&#xff08;以下简称“成都鼎桥”&#xff09;达成共建海鸥派的周边生态战略合作。基于海鸥派推出的鼎桥TD OS嵌入式发行版1.0&#xff0c;为海鸥派生态注入新的活力。 易百纳 Eba…

机器人增量学习研究综述

源自&#xff1a;控制与决策 作者&#xff1a;马旭淼 徐德 “人工智能技术与咨询” 发布 摘 要 机器人的应用场景正在不断更新换代,数据量也在日益增长.传统的机器学习方法难以适应动态的环境,而增量学习技术能够模拟人类的学习过程,使机器人能利用旧知识来加快新任务的…

数据分析处理的步骤是什么?制造业企业如何挑选数据分析处理软件?看这篇就够了

随着工业4.0的深入实施以及国家对制造业高质量发展战略的日益强调&#xff0c;工业数据已经崭露头角&#xff0c;成为生产经营活动中至关重要的核心要素。不仅如此&#xff0c;工业数据还作为优质的生产要素&#xff0c;为新兴生产力的形成提供了强有力的支撑&#xff0c;从而推…

docker自建GitLab仓库

摘要 GitLab 是一个功能强大的开源代码托管平台&#xff0c;它不仅提供了代码存储和版本控制的核心功能&#xff0c;还集成了项目管理、CI/CD 流水线、代码审查等企业级特性。本文将指导你如何在自己的服务器上搭建 GitLab 社区版&#xff0c;创建一个完全属于自己的开源仓库&…

Linux基础命令(续)

17&#xff0c;wc命令 作用&#xff1a;统计行数、单词数、字符个数 格式&#xff1a; wc 选项 文件 wc passwd 26 36 1159 passwd26&#xff1a;行数 36&#xff1a;单词数 1159&#xff1a;字符数 passwd&#xff1a;文件名wc autofs.conf 426 2604 15137 autofs.conf426…

某攻防演练心得之随笔记

最近太忙了&#xff0c;忙于各种奇奇怪怪的事情&#xff0c;有攻防&#xff0c;有应急&#xff0c;有渗透&#xff0c;还成为了一段时间内的“word高级工程师”......有师傅说我现在更新的越来越慢了&#xff0c;是呀&#xff0c;其实我也不知道怎么了&#xff0c;每天各种新闻…

用balenaEtcher烧录ubuntu的iso文件都失败,所以选用了另一种烧录的软件Rufus,然后烧录成功了+安装ubuntu的坑

https://releases.ubuntu.com/bionic/进入网页下载ubuntu 选择烧录软件将下载的Ubuntu烧录到U盘中 之前用这个U盘烧录过一次&#xff0c;成功了&#xff0c;后来应该是U盘受损或者是什么其他原因使得用这个U盘总是烧录失败 换思路&#xff1a;由于一直使用balenaEtcher烧录ubu…

《四月女友》开启预售 “不想错过”鼓励情侣找回消失的爱

电影《四月女友》由中国电影集团公司进口&#xff0c;中国电影股份有限公司发行、译制&#xff0c;改编自川村元气同名小说&#xff0c;山田智和导演&#xff0c;佐藤健、长泽雅美、森七菜主演。《四月女友》今日发布“不想错过”版预告&#xff0c;预告中&#xff0c;佐藤健饰…

【论文阅读笔记】HermesSim(Code is not Natural Language) (Security 24)

个人博客地址 HermesSim [Security 24] 论文&#xff1a;《Code is not Natural Language: Unlock the Power of Semantics-Oriented Graph Representation for Binary Code Similarity Detection》 仓库&#xff1a;https://github.com/NSSL-SJTU/HermesSim 提出的问题 二…