【Linux操作系统】线程的基本知识和创建--循环创建多个子线程

本篇文章主要介绍了线程的概念和作用,线程三级映射的实现,创建线程的方法(讲解pthread_self和pthread_create函数),循环创建多个子线程为例子,同时分析线程之间的全局变量的共享问题,希望可以帮助你。

在这里插入图片描述

文章目录

  • 一、线程的概念及其作用
    • 作用:
    • 三级映射
  • 二、线程的共享和非共享
    • 共享线程
    • 非共享线程
  • 三、创建线程的方法
    • 相关函数
      • 1. `pthread_self()`:
      • 2. `pthread_create`:
    • 继承Thread类
    • 实现Runnable接口
  • 四、循环创建多个子线程
    • 步骤
    • 示例
    • 代码解释
  • 线程间全局变量共享


一、线程的概念及其作用

在计算机科学中,线程是进程中的一个执行单元。一个进程可以拥有多个线程,每个线程都可以独立执行不同的任务。线程是实现并发执行的重要工具,可以提高程序的效率和性能。

作用:

  1. 提高程序的响应性:通过多线程可以同时执行多个任务,提高程序的响应速度,避免因为一个任务的阻塞而导致整个程序的停顿。

  2. 提高计算机资源的利用率:多线程可以充分利用计算机的多核处理器,同时执行多个任务,提高计算机资源的利用率。

  3. 实现并发编程:线程可以用于实现并发编程,例如在服务器中同时处理多个客户端请求,或者在图形界面程序中同时响应用户的输入等。

  4. 实现任务的分解和协作:通过将一个复杂任务分解为多个子任务,每个子任务由一个线程执行,可以提高程序的可维护性和可扩展性。线程之间可以通过共享内存或消息传递等方式进行协作和通信。

在这里插入图片描述


三级映射

在操作系统中,线程的运行需要通过三级映射来实现。这三级映射包括虚拟地址到物理地址的映射、物理地址到主存的映射、以及主存到磁盘的映射。

  1. 虚拟地址到物理地址的映射:每个线程都有自己的虚拟地址空间,虚拟地址是线程在运行时使用的地址。操作系统通过页表来实现虚拟地址到物理地址的映射。页表将虚拟地址划分为一系列的页面,并将这些页面映射到物理地址上的页框。当线程访问虚拟地址时,操作系统会根据页表将虚拟地址转换为物理地址。

  2. 物理地址到主存的映射:物理地址是实际的硬件地址,表示计算机中的内存地址。操作系统管理物理地址和主存之间的映射关系,确保线程可以访问到正确的主存地址。操作系统会将物理地址分配给不同的线程,以便线程可以在主存中存储和访问数据。

  3. 主存到磁盘的映射:主存是计算机中的临时存储器,用于存储线程运行时所需的数据和指令。但是主存的容量是有限的,当主存不足以存储所有线程需要的数据时,操作系统会将部分数据存储到磁盘上的虚拟内存中。这样,当线程需要访问被存储在磁盘上的数据时,操作系统会将数据从磁盘读取到主存中,然后线程可以继续访问这些数据。


二、线程的共享和非共享

共享线程

共享线程是指多个线程可以访问和修改同一进程的共享资源。共享线程通常用于多个线程之间需要共享数据和进行协作的场景。

在共享线程中,多个线程可以同时读取和修改同一份共享数据。这种共享数据可以是 全局变量、静态变量、堆内存中的对象等 。共享线程可以通过对共享资源的读写操作来实现线程之间的通信和协作。

共享线程的优点是可以方便地共享数据和协作,但同时也需要注意线程安全的问题。多个线程同时读写共享资源可能会导致数据的不一致性和竞态条件等问题,需要通过加锁、使用同步机制等方式来保证共享资源的正确性和一致性。

非共享线程

非共享线程是指每个线程拥有自己独立的资源,其他线程无法访问和修改。非共享线程通常用于执行独立的任务,不需要与其他线程进行通信和共享数据。

在非共享线程中,每个线程拥有自己独立的 栈空间、寄存器 等资源。这意味着每个线程都可以独立地执行自己的任务,互不干扰。非共享线程的优点是可以提高程序的并发性和执行效率,但同时也限制了线程之间的通信和协作。


三、创建线程的方法

相关函数

我们先来认识两个关于创建线程的函数:pthread_selfpthread_create


1. pthread_self():

pthread_self函数作用是获取当前线程的线程ID

函数原型:

pthread_t pthread_self(void);

该函数没有参数。

返回值:
返回调用线程的线程ID。


2. pthread_create

pthread_create函数的作用是创建一个新的线程,并将其加入到进程中。通过该函数,我们可以实现多线程的并发执行。

函数原型:

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

参数:

  • thread:指向线程ID的指针,用于存储新创建的线程的ID。
  • attr:指向线程属性的指针,用于设置新创建的线程的属性。可以传入NULL,使用默认属性。
  • start_routine:指向线程函数的指针,新创建的线程将从该函数开始执行。
  • arg:传递给线程函数的参数,可以是任意类型的指针。

返回值:
成功创建线程时,返回0;创建线程失败时,返回一个非零的错误代码。

示例和解释:

#include <stdio.h>
#include <pthread.h>void* thread_func(void* arg) {int thread_num = *(int*)arg;printf("Thread %d is running\n", thread_num);return NULL;
}int main() {pthread_t thread1, thread2;int arg1 = 1, arg2 = 2;pthread_create(&thread1, NULL, thread_func, &arg1);pthread_create(&thread2, NULL, thread_func, &arg2);pthread_join(thread1, NULL);pthread_join(thread2, NULL);return 0;
}

在上述示例中,我们定义了一个线程函数thread_func,该函数接收一个整数参数,并将其打印出来。在main函数中,我们创建了两个线程,并分别传递不同的参数给它们。通过pthread_create函数,我们将线程函数和参数传递给新创建的线程。然后,我们使用pthread_join函数等待这两个线程执行完成。

pthread_create函数用于创建一个新线程,并将其加入到进程中。该函数需要传入线程ID指针、线程属性、线程函数和参数。成功创建线程后,新线程将从指定的线程函数开始执行,并且可以访问传递给线程函数的参数。在上述示例中,我们可以看到两个线程分别打印了传递给它们的参数,说明它们成功接收到了参数并进行了相应的处理。


在大多数编程语言中,创建线程通常有两种方法:继承Thread类和实现Runnable接口。

继承Thread类

继承Thread类是一种创建线程的简单方法。通过继承Thread类,可以重写run()方法来定义线程的执行逻辑。

以下是一个使用继承Thread类创建线程的示例:

#include <stdio.h>
#include <pthread.h>// 继承Thread类,重写run()方法
void* thread_func(void* arg) {// 线程的执行逻辑printf("Hello from thread!\n");return NULL;
}int main() {// 创建线程实例pthread_t thread;// 启动线程pthread_create(&thread, NULL, thread_func, NULL);// 等待线程执行完成pthread_join(thread, NULL);return 0;
}

在上述示例中,我们通过继承Thread类,重写了run()方法,并在其中定义了线程的执行逻辑。然后使用pthread_create()函数创建了一个线程实例,并通过pthread_join()函数等待线程执行完成。

实现Runnable接口

实现Runnable接口是另一种创建线程的常用方法。通过实现Runnable接口,可以将线程的执行逻辑定义在run()方法中。

以下是一个使用实现Runnable接口创建线程的示例:

#include <stdio.h>
#include <pthread.h>// 实现Runnable接口,定义run()方法
void* thread_func(void* arg) {// 线程的执行逻辑printf("Hello from thread!\n");return NULL;
}int main() {// 创建线程实例pthread_t thread;pthread_attr_t attr;pthread_attr_init(&attr);// 创建线程对象pthread_create(&thread, &attr, thread_func, NULL);// 等待线程执行完成pthread_join(thread, NULL);return 0;
}

在上述示例中,我们通过实现Runnable接口,定义了run()方法,并在其中定义了线程的执行逻辑。然后使用pthread_create()函数创建了一个线程实例,并通过pthread_join()函数等待线程执行完成。


四、循环创建多个子线程

步骤

  1. 定义一个线程函数,该函数包含需要在子线程中执行的逻辑。可以在该函数中使用线程参数来区分不同的子线程。

  2. 在主函数中,使用pthread_create函数循环创建多个子线程。每个子线程都调用同一个线程函数。

  3. pthread_create函数中,将线程函数作为线程函数指针传递,并将对应的参数传递给线程函数。可以使用结构体或指针等方式传递参数。

  4. 在线程函数中,根据传递的参数执行相应的逻辑。

示例

下面是一个使用C语言实现的示例代码,演示了如何循环创建多个子线程,每个子线程都继承自同一个线程函数的方法:

#include <stdio.h>
#include <pthread.h>// 线程函数
void* threadFunc(void* arg) {int thread_num = *(int*)arg;printf("Thread %d is running\n", thread_num);// 执行线程的逻辑// ...return NULL;
}int main() {const int NUM_THREADS = 5;pthread_t threads[NUM_THREADS];for (int i = 0; i < NUM_THREADS; i++) {int* thread_arg = malloc(sizeof(int));*thread_arg = i + 1;pthread_create(&threads[i], NULL, threadFunc, thread_arg);}for (int i = 0; i < NUM_THREADS; i++) {pthread_join(threads[i], NULL);}return 0;
}

代码解释

在上述示例中,我们定义了一个线程函数threadFunc,其中包含了需要在子线程中执行的逻辑。在主函数中,我们使用pthread_create函数循环创建了5个子线程。每个子线程都调用threadFunc函数,并将对应的参数传递给线程函数。在threadFunc函数中,我们根据传递的参数执行线程的逻辑。


线程间全局变量共享

在多线程编程中,线程间的数据共享是一个常见的问题。线程间可以通过共享内存或全局变量来实现数据的共享。

以下是一个使用全局变量实现线程间数据共享的示例:

#include <stdio.h>
#include <pthread.h>// 全局变量
int count = 0;// 实现Runnable接口,定义run()方法
void* thread_func(void* arg) {// 访问和修改全局变量count++;return NULL;int main() {// 创建线程实例pthread_t thread1, thread2;pthread_attr_t attr;pthread_attr_init(&attr);// 创建线程对象pthread_create(&thread1, &attr, thread_func, NULL);pthread_create(&thread2, &attr, thread_func, NULL);// 等待线程执行完成pthread_join(thread1, NULL);pthread_join(thread2, NULL);// 打印全局变量的值printf("Count: %d\n", count);return 0;
}

在上述示例中,我们使用全局变量count来实现线程间的数据共享。在每个线程中,我们递增count的值。然后在主线程中打印count的值。由于count是全局变量,所有线程都可以访问和修改它,因此最终打印的count的值可能不是预期的结果。

在这里插入图片描述

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

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

相关文章

Redis笔记——(狂神说)待续

Nosql概述 为什么要用NoSql&#xff1f; 1、单机mysql的年代&#xff1a;90年代&#xff0c;网站访问量小&#xff0c;很多使用静态网页html写的&#xff0c;服务器没压力。 当时瓶颈是&#xff1a;1)数据量太大一个机器放不下。2)数据的索引(BTree)&#xff0c;一个机器内存也…

Git最简入门

文章目录 几个基本概念版本控制Git的由来分布式 vs 集中式GitSVN Git、GitHub、GitLab、GitWeb、Gitee的区别 动手进行版本控制初始化Git使用情景一&#xff1a;开发新项目使用情景二&#xff1a;在已有项目上开发设置代理 参考 几个基本概念 版本控制 在工作学习中&#xff…

LiveData相关基本使用及去除黏性数据的方法

目录 一、LiveData的基本使用1. 使用方式一2. 使用方式二3. 使用方式三 二、LiveData 去除黏性数据的方法1. 去除黏性的Java版本2. 去除黏性的Kotlin版本 一、LiveData的基本使用 1. 使用方式一 MyLiveData.kt package com.example.mylivedata.simple1import androidx.lifec…

「Python|音视频处理|环境准备」如何在Windows系统下安装并配置音视频处理工具FFmpeg

本文主要介绍如何在Windows系统下安装并配置音视频处理工具FFmpeg&#xff0c;方便使用python进行音视频相关的下载或编辑处理。 文章目录 一、下载软件二、解压并配置三、验证安装 一、下载软件 首先要去 ffmpeg官网 下载软件包 由于上面直接下载的按钮是.tar.xz格式的。为了…

Pygame编程(8)image模块

Pygame编程&#xff08;8&#xff09;image模块 函数示例 函数 pygame.image.load 从文件&#xff08;或类似文件的对象&#xff09;加载新图像load(filename) -> Surfaceload(fileobj, namehint“”) -> Surface pygame.image.save 将图像保存到文件&#xff08;或类似…

基于 Alpine 环境源码构建 alibaba-tengine(阿里巴巴)的 Docker 镜像

About Alpine&#xff08;简介&#xff09; Alpine Linux 是一款极其轻量级的 Linux 发行版&#xff0c;基于 busybox&#xff0c;多被当做 Docker 镜像的底包&#xff08;基础镜像&#xff09;&#xff0c;在使用容器时或多或少都会接触到此系统&#xff0c;本篇文章我们以该镜…

Pydev·离线git包

Pydev离线git包 1.下载离线git包&#xff1a;eclipse.egit.repository-4.4.0.201606070830-r.zip 2.将解压后目录&#xff1a;eclipse.egit.repository-4.4.0.201606070830-r\plugins下的jar文件放到 ide\eclipse\plugins目录下 3.重启pydevIDE 百度搜索站长工具&#xff1a;h…

head 请求了解过吗?如何用 get 模拟 head 请求?不需要服务器返回数据,怎么实现?

HEAD请求是HTTP/1.1协议中定义的一个请求方法&#xff0c;与GET请求相似&#xff0c;但只请求目标URL的头部&#xff0c;不请求实际的数据或者说正文内容。其主要用途是&#xff1a; 检查资源是否存在。获取资源的元数据&#xff08;如响应头中的Content-Length或Last-Modifie…

Spring集成【MyBatis】和【PageHelper分页插件】整合---详细介绍

一&#xff0c;spring集成Mybatis的概念 Spring 整合 MyBatis 是将 MyBatis 数据访问框架与 Spring 框架进行集成&#xff0c;以实现更便捷的开发和管理。在集成过程中&#xff0c;Spring 提供了许多特性和功能&#xff0c;如依赖注入、声明式事务管理、AOP 等 它所带来给我们的…

校对的力量:当专业遇上细节,文字焕发新生

在这个信息爆炸的时代&#xff0c;文字成为了我们传达思想、展现形象的重要工具。从新闻稿、政府材料到商业文档&#xff0c;其背后的准确性和专业性往往决定了信息传递的效果。而保证这一切的&#xff0c;就是细致入微的校对工作。 1.错别字与校对&#xff1a;细节之美 错别字…

go web框架 gin-gonic源码解读03————middleware

go web框架 gin-gonic源码解读03————middleware&#xff08;context&#xff09; 今天打完游戏有空整理整理之前看的gin的中间件设计&#xff0c;go的中间件设计相较于前两站还是蛮简单&#xff0c;蛮容易看懂的&#xff0c;所以顺便把context也一起写一下。 中间件是现在w…

开源全球地理空间数据可视化框架——Cesium学习(2023.8.21)

Cesium学习 2023.8.21 1、Cesium简介1.1 Github上的Cesium 2、Cesium下载安装使用2.1 方式一&#xff1a;页面在线引用2.2 方式二&#xff1a;页面离线使用2.3 方式三&#xff1a;完整项目使用 3、CesiumJS学习教程&#xff08;快速上手 API文档&#xff09;3、Cesium官方示例…

Python 读写 Excel 文件库推荐和使用教程

文章目录 前言Python 读写 Excel 库简介openpyxl 处理 Excel 文件教程pandas 处理 Excel 文件教程总结 前言 Python 读写 Excel 文件的库总体看还是很多的&#xff0c; 各有其优缺点&#xff0c; 以下用一图总结各库的优缺点&#xff0c; 同时对整体友好的库重点介绍其使用教程…

java中多个list怎么用List表示?

如果你有多个List对象&#xff0c;想要将它们合并成一个List对象&#xff0c;可以使用addAll()方法来实现。addAll()方法将会把一个List中的元素逐个添加到另一个List中。 以下是一个示例&#xff0c;展示了如何将多个List对象合并为一个List对象&#xff1a; import java.ut…

vue离线缓存资源文件

本文章主要是解决大文件,实时请求资源浪费网络资源的问题 从而有效的将解决用户体验的问题 话不多说上才艺 ⬇️⬇️⬇️⬇️⬇️⬇️⬇️ 找到项目中的 index.html 文件,并在 html 标签中加入 manifest"manifest.appcache" 安装 appcache-manifest 包 npm ins…

【NPM】包的指令

npm 安装的包可以根据其用途和作用进行分类&#xff0c;一般可以分为以下几种类型&#xff1a; 普通依赖&#xff08;Regular Dependencies&#xff09;&#xff1a; 这些是你项目中的实际依赖项&#xff0c;用于构建、运行或扩展你的应用程序。这些依赖会被包含在你的应用程序…

c++ qt--信号与槽(二) (第四部分)

c qt–信号与槽(二) &#xff08;第四部分&#xff09; 信号与槽的关系 1.一对一 2.一对多 3.多对一 4.多对多 还可以进行传递 信号->信号->槽 一个信号控制多个槽的例子&#xff08;通过水平滑块控制两个组件&#xff09; 1.应用的组件 注意这里最下面的组件进行…

【Qt学习】06:事件与事件过滤器

OVERVIEW 事件与事件过滤器一、事件1.鼠标事件创建子类MyLabel重写鼠标事件提升Label控件为MyLabel 2.定时器事件timerEventQTimer 3.事件分发器&#xff08;event函数&#xff09;event函数重写event函数深入 二、事件过滤器1.事件过滤器2.事件处理的五个层次 事件与事件过滤器…

Tomcat和Servlet基础知识的讲解(JavaEE初阶系列16)

目录 前言&#xff1a; 1.Tomcat 1.1Tomcat是什么 1.2下载安装 2.Servlet 2.1什么是Servlet 2.2使用Servlet来编写一个“hello world” 1.2.1创建项目&#xff08;Maven&#xff09; 1.2.2引入依赖&#xff08;Servlet&#xff09; 1.2.3创建目录&#xff08;webapp&a…

VR法治警示教育:情景式课堂增强教育效果

VR法治警示教育平台是一款基于虚拟现实技术的在线教育平台&#xff0c;旨在通过模拟真实场景和互动体验&#xff0c;向公众普及法律知识&#xff0c;提高公民的法律意识和素养。该平台采用先进的虚拟现实技术&#xff0c;将用户带入一个逼真的仿真环境&#xff0c;让用户身临其…