【linux学习】多线程(1)

news2025/1/18 10:04:42

文章目录

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

线程的概念

在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释放另一个锁。
使用超时机制:在尝试获取锁时设置超时。如果线程不能在规定的时间内获得锁,它将放弃并稍后重试。这可以防止线程无限期地等待,从而增加了系统的灵活性。
减少锁的粒度:尽量只锁定需要保护的最小资源范围。例如,如果你可以只锁定一个数据结构的一部分而不是整个数据结构,那么这将减少死锁的可能性。
避免长时间持有锁:尽量缩短持有锁的时间。这意味着你应该在获得锁后尽快完成你的工作并释放锁。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

只需三步将Kimi接入微信公众号

今天我将手把手交大家如何把Kimi大模型接入微信公众号&#xff0c;创建属于你自己的公众号智能助理&#xff0c;让你的公众号具备智能对话、文件阅读、信息搜索等强大功能&#xff0c;同时提高用户互动率、减少人工客服压力等。 废话不多说&#xff0c;先来看看实际效果吧~ 一…

简约在线生成短网址系统源码 短链防红域名系统 带后台

简约在线生成短网址系统源码 短链防红域名系统 带后台 安装教程&#xff1a;访问 http://你的域名/install 进行安装 源码免费下载地址抄笔记 (chaobiji.cn)https://chaobiji.cn/

AI视频教程下载:用ChatGPT自动化各种工作任务

这是一门实用的无代码课程&#xff0c;旨在通过使用ChatGPT高级数据分析和代码解释器提高生产力。 通过让ChatGPT代码解释器创建程序来自动化单调的任务&#xff0c;提高您的计算机生产力。 这门课程专为那些渴望快速使用小型实用程序的人设计&#xff0c;不需要编程知识。相…

下水道井盖多分类检测定位

下水道井盖识别&#xff0c;多分类&#xff0c;使用yolov5训练&#xff0c;采用一部分开源数据集和自建数据集。python pytorch opencv 深度学习#人工智能#深度学习#目标检测

在另外一个页面,让另外一个页面弹框显示操作(调佣公共的弹框)

大概意思是&#xff0c;登录弹框在另外一个页面中&#xff0c;而当前页面不存在&#xff0c;在当前页面中判断如果token不存在&#xff0c;就弹框出登录的弹框 最后一行 window.location.href … 如果当前用户已登录&#xff0c;则执行后续操作(注意此处&#xff0c;可不要)

7.STL_string1.0(详细)

目录 1. 什么是STL 2. STL的版本 3. STL的六大组件 1. 为什么学习string类&#xff1f; 1.1 C语言中的字符串 2. 标准库中的string类 2.1 string类(了解) 2.2 string类的常用接口说明 1. string类对象的常见构造 2. string类对象的容量操作 reserve 3. string类对象…

省级生活垃圾无害化处理率面板数据(2004-2022年)

01、数据简介 生活垃圾无害化处理率是指经过处理的生活垃圾中&#xff0c;达到无害化标准的垃圾所占的比例。这一指标是衡量城市垃圾处理水平的重要标准&#xff0c;反映了城市对垃圾进行有效管理和处理的能力。 生活垃圾无害化处理的主要方式包括生活垃圾焚烧、生活垃圾卫生…

数据挖掘原理与应用------分类预测

在数据挖掘和机器学习领域&#xff0c;TPR&#xff08;True Positive Rate&#xff09;是指在实际为阳性的情况下&#xff0c;模型正确预测为阳性的比例。TPR也被称为灵敏度&#xff08;Sensitivity&#xff09;或召回率&#xff08;Recall&#xff09;。它是评估分类模型性能的…

构建教育新未来:智慧校园平台的深度解读与全景呈现

引言 在全球数字化转型的大潮中&#xff0c;智慧校园平台作为教育信息化的重要载体&#xff0c;正以前所未有的姿态颠覆传统的教育模式&#xff0c;引领教育行业步入一个崭新的时代。这个融合了大数据、人工智能、云计算、物联网等一系列前沿科技的平台&#xff0c;以其强大的功…

QT day2 作业

头文件 #ifndef MYWIDGET_H #define MYWIDGET_H#include <QWidget> #include <QDebug> #include<QIcon> #include<QLabel> #include<QMovie> #include<QLineEdit> #include<QPushButton> QT_BEGIN_NAMESPACE namespace Ui { class …

浅谈SiC MOSFET之MOSFET

1.掺杂后的半导体 P型半导体&#xff0c;多子是空穴&#xff0c;少子是自由电子。 N型半导体&#xff0c;多子是自由电子&#xff0c;少子是空穴。 2.电中性 尽管他们分别有着空穴带正电&#xff0c;自由电子带负电&#xff0c;但是整体上是电中性的。 以P型半导体为例&…

Leaflet.canvaslabel在Ajax异步请求时bindPopup无效的解决办法

目录 前言 一、场景重现 1、遇到问题的代码 2、问题排查 二、通过实验验证猜想 1、排查LayerGroup和FeatureGroup 2、排查Leaflet.canvaslabel.js 三、柳暗花明又一村 1、点聚类的办法 2、歪打正着 总结 前言 在上一篇博客中介绍了基于SpringBoot的全国风景区WebGIS按…

实验室纳新宣讲会(java后端)

前言 2024-5-12 22:00:39 这是陈旧已久的草稿 2021-09-16 15:41:38 发布一下 当时我进入实验室&#xff0c;也是大二了&#xff0c;实验室纳新需要宣讲&#xff0c; 但是当时有疫情&#xff0c;又没宣讲成。 实验室纳新宣讲会&#xff08;java后端&#xff09; 首先&#x…

重写muduo之TcpConnection

目录 1、 TcpConnection.h 2、 TcpConnection.cc 1、 TcpConnection.h TcpConnection底层绑定&#xff08;管理&#xff09;了一个Channel&#xff0c;Channel有事件被Poller通知后&#xff0c;会调用相应的回调&#xff0c;这些回调也是TcpConnection中包含的方法&#xff0c…

国产操作系统下Chrome的命令行使用 _ 统信 _ 麒麟

原文链接&#xff1a;国产操作系统下Chrome的命令行使用 | 统信 | 麒麟 Hello&#xff0c;大家好啊&#xff01;今天我们来聊聊如何在国产操作系统上使用命令行操作Google Chrome。无论是进行自动化测试、网页截图还是网页数据抓取&#xff0c;使用命令行操作Google Chrome都能…

房屋出租管理系统需求分析及功能介绍

房屋租赁管理系统适用于写字楼、办公楼、厂区、园区、商城、公寓等商办商业不动产的租赁管理及租赁营销&#xff1b;提供资产管理&#xff0c;合同管理&#xff0c;租赁管理&#xff0c; 物业管理&#xff0c;门禁管理等一体化的运营管理平台&#xff0c;提高项目方管理运营效率…

51输出周期为40ms的方波(C+汇编)

题目 已知Fosc12MHz&#xff0c;T1工作于方式1&#xff0c; ①&#xff1a;实现20ms延时&#xff0c;求定时器初值TH0&#xff1f;TL0&#xff1f;写出具体的计算过程。 ②&#xff1a;利用汇编或C语言编程实现输出周期为40ms的方波。 周期为40ms的方波&#xff0c;半周期就…

纯CSS实现步骤条

纯CSS实现纵向Steps步骤条效果 效果图 实现思路 步骤条是一种用于引导用户按照特定流程完成任务的导航条&#xff0c;在各种分步表单交互场景中广泛应用。步骤条通常由编号、名称和引导线三个基本要素组成。本文中要实现的是一个简单的步骤条&#xff0c;包含上述三个基本要素…

Leetcode经典题目之用队列实现栈

P. S.&#xff1a;以下代码均在VS2019环境下测试&#xff0c;不代表所有编译器均可通过。 P. S.&#xff1a;测试代码均未展示头文件stdio.h的声明&#xff0c;使用时请自行添加。 目录 1、题目展示2、题目分析3、完整代码演示4、结语 1、题目展示 前面我们了解过如何实现队列…

webservice和TCP类型接口测试

1.webservice类型接口 1.1.webservice类型接口介绍 Web服务&#xff08;WebService&#xff09;是一种基于网络的应用程序接口&#xff08;API&#xff09;&#xff0c;可通过网络来进行通信和交互。它们使用标准化的协议和格式来进行通信&#xff0c;最常见的是使用XML&#…