如何在 Bash 脚本中使用多线程编程
开发人员一直对多线程编程很感兴趣,主要用来提高应用程序性能和优化资源的使用。本篇将向您介绍 Bash 多线程编程的基础知识。
什么是多线程编程
?
一张图胜过千言万语,当谈到 Bash 中单线程编程和多线程编程之间的区别时,使用图来解释要比文字方便的多:
sleep 1
sleep 1 & sleep 1
我们的第一个多线程编程使用单行脚本再简单不过了; 在第一行中,我们使用 sleep 1
命令休眠一秒钟。 就用户而言,单个线程正在执行一秒钟的单个睡眠。
在第二行,我们有两个睡眠 1 秒的命令。我们通过使用&
分隔符将它们连接起来,& 不仅作为两个sleep命令之间的分隔符,而且在 Bash 中指示第一个命令在后台线程中启动。
通常,我们会使用分号 ;
来终止命令。 这样做将执行命令,然后才继续执行分号后面列出的下一个命令。 例如,执行 sleep 1; sleep 1 只需要两秒钟多一点——第一个命令正好一秒钟,第二个命令一秒钟,并且两个命令中的每一个都需要少量的系统开销。
然而,除了使用分号终止命令之外,还可以使用 Bash 识别的其他命令终止符,如 &
、&&
和 ||
。 && 语法与多线程编程完全无关,它只是这样做:只有在第一个命令成功时才继续执行第二个命令。 ||
与&&
相反,仅当第一个命令失败时才执行第二个命令。
回到多线程编程,使用&作为我们的命令终止符将启动一个后台进程,执行它前面的命令。然后它立即继续在当前 shell 中执行下一个命令,同时让后台进程(线程)自行执行。
在命令的输出中,我们可以看到正在启动的后台进程(如 [1] 7136 所示,其中 7136 是刚刚启动的后台进程的进程 ID 或 PID,而 [1] 表示这是我们的第一个后台进程 ) 并随后被终止(如 [1]+ Done sleep 1 所示)。
现在让我们来证明一下我们是同时有效地运行两个sleep进程:
time sleep 1; echo 'done'
time $(sleep 1 & sleep 1); echo 'done'
在这里,我们在time下开始睡眠过程,我们可以看到在返回命令行提示符之前,单线程命令是如何运行1.003秒的。
然而,在第二个示例中,即使我们执行了两个睡眠周期(和进程),但它花费了大约相同的时间(1.005 秒),尽管不是连续的。 我们再次为第一个 sleep 命令使用后台进程,导致(半)并行执行,即多线程。
我们还在两个sleep
命令周围使用了子shell包装器$(…)
,以便在time下将它们组合在一起。正如我们可以看到的,我们的输出显示是在1.005秒内执行完成,因此两个sleep 1命令肯定是同时运行的。有趣的是,总体处理时间(0.002秒)的增长非常小,这可以很容易地用启动子shell所需的时间和启动后台进程所需的时间来解释。
多线程进程和后台进程管理
在 Bash 中,多线程编程通常会涉及来自主单行脚本或完整 Bash 脚本的后台线程。 从本质上讲,可以将 Bash 中的多线程编程视为启动多个后台线程。 当人们开始使用多个线程进行编码时,很快就会清楚这些线程通常需要一些处理。 例如,假设我们在 Bash 脚本中启动五个并发的 sleep 周期(和进程);
rest.sh
#!/bin/bash sleep 10 & sleep 600 & sleep 1200 & sleep 1800 & sleep 3600 &
当我们启动脚本时(在使用chmod +x rest.sh
使其具有可执行权限后),我们看不到任何输出!即使我们执行 jobs
命令(显示任何正在进行的后台作业的命令),也没有输出。这是为什么?
原因是,用于启动此脚本的shell(即当前shell)与执行实际 sleep 命令或将其放在后台的 shell(也不是同一线程;从子shell的角度考虑,它们本身就是线程)不同。而是在执行./rest.sh
时启动的(子)shell。
现在我们修改 rest.sh 脚本,在脚本中添加 jobs
命令。这将确保 jobs 在相关的(子)shell 内执行,与 sleep 周期(和进程)开始的位置相同。
rest.sh
#!/bin/bash sleep 10 & sleep 600 & sleep 1200 & sleep 1800 & sleep 3600 & jobs
这一次,由于脚本末尾的jobs命令,我们可以看到正在启动的后台进程列表。我们还可以看到它们的PID(进程标识符)。在处理和管理后台进程时,这些PID非常重要。
获取后台进程标识符的另一种方法是在将程序/进程放入后台后立即查询它:
rest.sh
#!/bin/bash sleep 10 & echo ${!} sleep 600 & echo ${!} sleep 1200 & echo ${!} sleep 1800 & echo ${!} sleep 3600 & echo ${!}
类似于我们的 jobs 命令(当我们重新启动我们的 rest.sh 脚本时,显示进程有了新的 PID),由于 Bash ${!}
变量被回显,我们现在几乎可以在脚本启动后立即看到五个 PID: sleep 进程被一个接一个地放入后台线程。
wait 命令
一旦我们开始了我们的后台进程,我们就没有别的事可做了,只能等待它们完成。但是,当每个后台进程正在执行一个复杂的子任务,并且我们需要主脚本(启动后台进程)在一个或多个后台进程终止时恢复执行时,我们需要额外的代码来处理这个问题。
现在让我们使用wait
命令扩展脚本,来处理后台线程:
rest.sh
#!/bin/bash sleep 10 & T1=${!} sleep 600 & T2=${!} sleep 1200 & T3=${!} sleep 1800 & T4=${!} sleep 3600 & T5=${!} echo "This script started 5 background threads which are currently executing with PID's ${T1}, ${T2}, ${T3}, ${T4}, ${T5}." wait ${T1} echo "Thread 1 (sleep 10) with PID ${T1} has finished!" wait ${T2} echo "Thread 2 (sleep 600) with PID ${T2} has finished!"
在这里,我们用两个wait
命令扩展了我们的脚本,它们等待连接到第一个和第二个线程的PID终止。10秒后,我们的第一个线程就存在了,我们会收到同样的通知。这个脚本将一步一步地执行以下操作:几乎同时启动五个线程(尽管线程本身的启动仍然是顺序的,而不是并行的),其中五个 sleep 线程中的每一个都将并行执行。
然后,主脚本(按顺序)报告创建的线程,并随后等待第一个线程的进程ID终止。当这种情况发生时,它将依次报告第一个线程的完成情况,并开始等待第二个线程完成,等等。
当涉及到在Bash中并行运行多个线程(作为后台线程)时,使用Bash常用的用法 &
、${!}
和wait
命令为我们提供了极大的灵活性。
相关文章
在 Bash 中创建进度条
发布时间:2023/05/04 浏览次数:164 分类:操作系统
-
这是有关在 Bash 中创建进度条以显示正在运行的命令或进程的进度的指南。本文将探讨在 Bash(Linux 和 macOS 的默认 shell)中向 shell 脚本添加进度条的几种方法。使用 pv 命令在 Bash 中创建进度条
在 Bash 中将 Stderr 和 Stdout 重定向到一个文件
发布时间:2023/05/04 浏览次数:130 分类:操作系统
-
本文介绍了如何在 Bash 中重定向 stderr 和 stdout。让我们开始了解 Linux 中的标准输出和标准错误这两个术语。Linux 中的标准输出和标准错误 在 Linux 中,命令从用户那里获取一些输入,可以是文件
在 Bash 中删除重复行
发布时间:2023/05/04 浏览次数:54 分类:操作系统
-
这是一篇关于如何在 Bash 中消除文件中的重复行的文章。使用 sort 和 uniq 删除 Bash 中的重复行 重复条目会在 Bash 脚本中导致各种问题,例如不正确或不一致的结果,它们还会使脚本难以维护。
在 Bash 中计算目录中的文件
发布时间:2023/05/04 浏览次数:143 分类:操作系统
-
本文介绍如何使用 Bash 命令行统计特定目录下的文件数。计算一个目录中有多少文件是 Bash 中的一项常见任务,可以使用多种不同的工具和方法来执行此操作。
在 Git Bash 上打开一个文件
发布时间:2023/04/08 浏览次数:131 分类:Git
-
我们不能在 Git Bash 中使用 open。 如果您尝试在 Git Bash 上使用 open 打开一个文件,您将得到 bash: open: command not found 错误。
使用 Visual Studio Code 配置 Git Bash
发布时间:2023/03/31 浏览次数:122 分类:Git
-
本文概述了在 Windows 上使用 Visual Studio Code 配置 Git Bash 的必要步骤。
在 Git Bash 中更改驱动器
发布时间:2023/03/31 浏览次数:123 分类:Git
-
这篇简短的文章将讨论我们如何使用 Git Bash 在 Windows 操作系统中拥有一个 Unix 风格的命令行环境,并在这个终端上运行多个命令。
在 Mac 上打开 Git Bash
发布时间:2023/03/31 浏览次数:398 分类:Git
-
在 Git 中管理版本号 Git 是开发人员使用的最受欢迎和最著名的免费版本控制系统,可帮助他们在团队中安全高效地处理各种程序。 他们可以轻松地跟踪自己的项目,而无需探究彼此的任务。