线程池笔记

笔记梳理


前言

本篇博客是对我近期编写代码的一些简单笔记
在这里和大家分享分享
共勉


.PHONY

在Makefile中,.PHONY 是一个特殊的目标(target),它用于声明一些"伪目标"(phony targets)。伪目标并不是文件名,它们不对应磁盘上的实际文件。相反,它们用于执行一些命令,这些命令通常用于清理构建目录、安装软件、检查代码等。

当你声明一个目标为 .PHONY 时,你告诉 make 工具这个目标是"伪"的,即它不代表文件名。这对于防止 make 误将伪目标与文件名混淆非常重要。例如,如果你有一个名为 clean 的伪目标,用于删除构建过程中生成的文件,同时你也有一个名为 clean 的文件,那么如果不将 clean 声明为 .PHONY,当你运行 make clean 时,make 可能会认为你想要更新 clean 文件(即查找是否有规则可以生成或更新它),而不是执行你指定的清理命令。

cpp 复制代码
.PHONY: clean  
  
clean:  
    rm -f *.o myprogram

这个 Makefile 片段定义了一个 clean 伪目标,当运行 make clean 时,它会删除所有 .o 类型的对象和名为 myprogram 的可执行文件。通过将 clean 声明为 .PHONY,我们确保了无论是否存在名为 clean 的文件,上述命令都会被执行。

C++标准库头文件

#include < iostream>:

这是一个C++头文件,用于输入输出流操作。它包含了cin、cout、cerr和clog等对象,用于标准输入输出。

#include < fstream>:

同样是C++头文件,用于文件输入输出操作。它定义了ifstream(用于从文件读取)、ofstream(用于向文件写入)和fstream(同时支持读写)等类。

#include < string>:

C++头文件,提供了对字符串的支持。它定义了std::string类,用于表示和操作字符串。

C/C++通用或C特有头文件

#include < cstdarg>:

这是一个C++头文件,提供了对可变参数列表的支持。它主要用于C++中与va_list、va_start、va_arg和va_end宏一起使用,以处理函数中的可变数量参数。

可变参数函数允许你定义一个函数,该函数可以接受不确定数量的参数。这种函数通常至少有一个固定参数,这个固定参数通常用于确定可变参数的数量或类型。最常见的例子是printf函数 。)
代码示例:

cpp 复制代码
#include <stdio.h>  
#include <stdarg.h>  
  
// 定义一个可变参数函数  
void printNumbers(int num, ...) {  
    va_list args;  
    va_start(args, num); // 初始化args,num是最后一个固定参数  
  
    for (int i = 0; i < num; i++) {  
        int value = va_arg(args, int); // 获取下一个int类型的参数  
        printf("%d ", value);  
    }  
  
    va_end(args); // 清理  
    printf("\n");  
}  
  
int main() {  
    printNumbers(3, 1, 2, 3); // 输出: 1 2 3  
    printNumbers(5, 10, 20, 30, 40, 50); // 输出: 10 20 30 40 50  
    return 0;  
}

#include < ctime>:

这个头文件在C和C++中都可用,提供日期和时间的函数。例如,time()函数返回当前时间,localtime()函数将时间转换为本地时间等。

#include <unistd.h>:

这是一个POSIX标准的头文件,主要用于提供对POSIX操作系统API的访问。它定义了多种符号常量、类型和函数,如文件操作、进程控制、程序执行、内存管理、进程间通信等。它主要用于Unix/Linux系统,不是C标准库的一部分,但在许多C和C++程序中都会用到。

#include <sys/types.h>:

这个头文件提供了基本的数据类型定义,这些类型在POSIX系统编程中广泛使用。它通常与<sys/stat.h>等头文件一起使用,以提供对文件状态、进程控制等系统功能的访问。

#include <sys/stat.h>:

这个头文件提供了文件状态的结构体(如stat)和宏定义,以及与文件权限和文件类型测试相关的函数原型。它是系统编程中处理文件和目录的必备工具。

#include <fcntl.h>:

这个头文件定义了文件控制选项,这些选项用于open()、fcntl()等系统调用中,以控制文件的打开和访问方式。例如,O_RDONLY、O_WRONLY、O_CREAT等标志。

mkdir

在Linux中,mkdir命令用于创建新的目录(或文件夹)。它是Linux和Unix系统中非常基础且常用的命令之一。使用mkdir命令时,你可以一次性创建一个目录,也可以递归地创建多个目录。

基本用法

要创建一个新目录,你只需在终端中输入mkdir后跟目录名。例如,要创建一个名为MyFolder的目录,你可以使用以下命令:

cpp 复制代码
mkdir MyFolder

创建多级目录

默认情况下,mkdir命令不会创建多级目录(即目录中的目录)。如果你尝试创建一个不存在的父目录中的子目录,mkdir会报错。为了解决这个问题,你可以使用-p(或--parents)选项,这样mkdir就会创建所有必要的父目录。例如,要创建/home/user/docs/projects/new_project目录,你可以使用:

cpp 复制代码
mkdir -p /home/user/docs/projects/new_project

设置目录权限

虽然mkdir命令本身主要用于创建目录,但你可以通过结合使用umask命令或直接在mkdir命令中使用-m(或--mode)选项来设置新目录的权限。例如,要创建一个权限为755的新目录,你可以使用:

cpp 复制代码
mkdir -m 755 MyNewFolder

c_str()

c_str() 是 C++ 标准库中字符串类(如 std::string)的一个成员函数,它返回一个指向以 null 结尾的字符数组(C 风格字符串)的指针。这个指针指向的字符串是 std::string 对象内部存储的字符序列的一个副本,但这个副本是以 C 风格字符串的形式存在的,即最后一个字符是 null 字符(\0),用于标记字符串的结束。

使用 c_str() 的场景通常涉及需要 C 风格字符串的 C++ 代码,或者是需要与其他需要 C 风格字符串作为输入的 C 函数接口交互时。例如,在使用某些只接受 const char* 参数的 C 库函数时,就需要使用 c_str() 方法来获取 std::string 对象的 C 风格字符串表示。

需要注意的是,c_str() 返回的指针是指向内部数据的,因此必须保证在调用 c_str() 后的整个字符串对象生命周期内,这个指针都是有效的。一旦 std::string 对象被销毁或者内容被修改(比如通过赋值、append、replace 等操作),这个指针就可能指向无效的内存,使用这样的指针会导致未定义行为。

代码示例:

cpp 复制代码
#include <iostream>  
#include <string>  
  
int main() {  
    std::string str = "Hello, World!";  
    const char* cstr = str.c_str();  
  
    // 使用 cstr,比如输出  
    std::cout << cstr << std::endl;  
  
    // 注意:在 str 被修改之前,cstr 是有效的  
    // 如果在这里修改了 str,比如 str += "!";,那么 cstr 可能指向无效的内存  
  
    return 0;  
}

snprintf

snprintf是C语言中的一个标准库函数,用于将格式化的数据写入字符串。这个函数特别有用,因为它允许程序员指定目标字符串的最大长度,从而避免缓冲区溢出的风险。

函数原型:

cpp 复制代码
int snprintf(char *str, size_t size, const char *format, ...);

str:指向目标字符串的指针,即格式化后的数据将要存储的位置。
size:目标字符串的最大长度(包括结尾的空字符'\0')。如果提供的大小小于实际需要的字节数,那么结果将被截断。
format:格式化字符串,用于指定输出的格式。这与printf函数中的格式化字符串类似。
...:可变参数列表,根据格式化字符串中的占位符来填充数据。

成功时,返回写入到目标字符串的字符数(不包括空字符'\0')。
失败时,返回负数,表示出错的原因。

vsnprintf

vsnprintf 是一个在 C 语言标准库中定义的函数,用于将格式化的数据写入一个字符串缓冲区中,同时允许用户指定缓冲区的大小,以避免缓冲区溢出的风险。这个函数的行为类似于 printf,但提供了更高的安全性,因为它允许开发者控制输出的最大长度。

函数原型:

cpp 复制代码
int vsnprintf(char *str, size_t size, const char *format, va_list ap);

str:指向用于存储格式化输出的字符缓冲区的指针。
size:指定缓冲区 str 的大小,即最多可以写入的字符数(包括最后的空字符 '\0')。
format:格式化字符串,指定了后续参数如何被格式化和插入到输出字符串中。
ap:一个 va_list 类型的变量,包含了要格式化的可变参数列表。

函数功能:
vsnprintf 函数会读取 format 字符串,并根据其中的格式说明符来格式化 ap 列表中的参数。格式化后的字符串(不包括终止的空字符 '\0')会被写入 str 指向的缓冲区中,但写入的字符数不会超过 size - 1(以确保为终止的空字符留出空间)。如果格式化后的字符串长度超过了 size - 1,则超出的部分会被截断,并且函数会返回如果缓冲区足够大时应该写入的字符数(不包括终止的空字符)

umask

在Unix、Linux和其他类Unix操作系统中,umask(用户文件创建掩码)是一个系统调用,用于设置进程创建新文件或目录时的默认权限。当你执行umask(0);时,你实际上是在尝试将进程的umask值设置为0,但这在大多数系统上并不是一个有效的操作,因为umask值用于指定哪些权限位应该被清除(即设置为0),而不是直接设置权限。

umask的基本概念

定义:umask(user-file-creation mode mask)是Unix、Linux系统中用于控制新文件和目录默认权限的命令。

取值范围:umask值由三个八进制数字组成,取值范围是000-077(或写作0000-0777,但通常省略前导的0)。

作用:umask值用于从文件和目录的默认权限中减去相应的权限位,从而确定新创建文件或目录的实际权限。

umask的工作原理

对于文件,系统默认的基础权限是666(即-rw-rw-rw-),然后根据umask值禁用相应的权限位。例如,如果umask值为022,则新创建文件的默认权限为666-022=644(即-rw-r--r--)。

对于目录,系统默认的基础权限是777(即-rwxrwxrwx),然后根据umask值禁用相应的权限位。例如,如果umask值为022,则新创建目录的默认权限为777-022=755(即-rwxr-xr-x)。

umask的设置方法
临时设置:

在shell中直接执行umask命令后跟一个八进制数,可以临时更改当前shell会话的umask值。例如,umask 022会将umask值设置为022,但这仅对当前shell会话有效,重启系统或开启新的shell会话后,umask值将恢复为默认值。
永久设置:

要永久更改umask值,需要修改用户的shell配置文件(如.bashrc、.bash_profile、.zshrc等),或系统的全局配置文件(如/etc/profile、/etc/bashrc等)。在这些文件中添加一行umask命令,并在用户登录时自动执行,从而实现永久更改。

注意事项

umask值只能用于禁用权限,而不能用于启用特殊权限(如setuid、setgid和sticky bit)。

umask的设置位置和数值会影响新创建文件或目录的默认权限,因此在进行系统配置时需要谨慎考虑。

如果遇到umask不生效的情况,可能是由于umask值设置不正确、设置位置不当或受到其他系统配置的影响。此时,需要仔细检查umask的设置情况,并参考系统文档或寻求专业帮助来解决问题

open函数

在Linux系统中,open函数是用于打开和可能创建文件的系统调用。该函数接受多个标志(flags)作为参数,以指定文件的打开模式、权限等

O_RDONLY

意思:以只读方式打开文件。如果文件成功打开,则只能进行读操作,不能进行写操作。

O_WRONLY

意思:以只写方式打开文件。如果文件成功打开,则只能进行写操作,不能进行读操作。如果文件不存在且没有指定O_CREAT标志,则open调用会失败。

O_RDWR

意思:以读写方式打开文件。如果文件成功打开,则既可以进行读操作,也可以进行写操作。

O_CREAT

意思:如果文件不存在,则创建该文件。通常与O_WRONLY、O_RDWR或O_RDONLY结合使用,但需要注意的是,仅当没有指定O_RDONLY且文件不存在时,O_CREAT才会导致创建文件。另外,O_CREAT标志需要一个额外的参数(通常是文件权限),该参数指定了新创建文件的权限。

O_APPEND:

在文件末尾追加数据,而不是覆盖现有内容。

O_TRUNC:

如果文件已经存在,并且是以写模式(O_WRONLY、O_RDWR)打开的,则将其截断为空文件。

O_EXCL:

与O_CREAT一起使用时,如果文件已经存在,则open调用将失败。这用于确保打开的文件是新创建的。

O_SYNC:

使文件写操作变为同步写入,即将数据立即写入磁盘。这可以提高数据的安全性,但可能会降低性能。

O_NONBLOCK:

以非阻塞方式打开文件,即使无法立即进行读写操作也不会被阻塞。

可变参数列表

cpp 复制代码
void LogMessage(int level, const char *format, ...);

在C和C++中,函数声明或定义中的省略号(...)被称为可变参数列表(variadic

arguments)或可变数量的参数。它允许函数接受一个固定数量的参数之后,再接受任意数量的参数。这种机制通常与stdarg.h(在C中)或cstdarg(在C++中)头文件中的宏一起使用,以便在函数体内访问这些额外的参数

va_start

va_start 是 C 和 C++ 中用于处理可变参数列表(variadic arguments)的一个宏,它定义在

<stdarg.h>(C)或 (C++)头文件中。va_start 的主要作用是初始化一个 va_list

类型的变量,以便你可以使用 va_arg 和 va_end 宏来遍历和访问函数中的可变参数。

cpp 复制代码
void va_start(va_list ap, last_fixed_arg);

ap 是一个 va_list 类型的变量,它将被初始化为指向可变参数列表的第一个参数。

last_fixed_arg 是可变参数列表中最后一个固定参数(即不是可变参数的一部分)的名称。这个参数的存在是为了让编译器能够确定可变参数列表在栈上的起始位置。

va_end

va_end 的主要目的是在访问完可变参数列表后,清理与 va_list 变量相关联的任何资源或状态

functional

#include 是 C++ 标准库中的一个预处理指令,它告诉编译器包含(或导入)头文件。这个头文件定义了一系列用于支持函数对象、引用封装器(reference wrappers)、绑定器(binders)、以及用于执行回调、延迟执行和其他函数式编程技术的模板和类。

头文件中最著名的特性之一是 std::function 类模板,它提供了一种通用的方式来存储、复制和调用任何可调用的目标(Callable Target),比如函数、Lambda 表达式、函数对象、以及绑定表达式。这使得 std::function 成为实现回调机制、事件处理器、以及策略模式等高级编程技术的有力工具。

此外, 还定义了 std::bind 函数模板,它允许你将可调用对象与一组参数进行绑定,生成一个新的可调用对象。这个新对象在被调用时,会将其绑定的参数与传递给它的任何额外参数一起传递给原始的可调用对象。

还提供了 std::ref 和 std::cref,它们分别用于创建左值引用和右值引用的封装器,以便可以将引用传递给 std::bind 或 std::function 等机制,而不会导致参数被复制或移动。

最后, 还包含了一些预定义的函数对象模板,如 std::plus<>、std::minus<>、std::multiplies<> 和 std::divides<> 等,它们分别实现了加、减、乘、除等基本算术运算。这些函数对象模板可以与标准算法(如 std::transform)一起使用,以实现各种数据转换和处理任务。

static_cast

static_cast 是 C++ 中用于基本数据类型转换、类层次结构中对象的向上转换(即从派生类指针或引用转换为基类指针或引用)以及执行用户定义的类型转换(如类中的转换构造函数或转换运算符)的运算符。它是一种显式类型转换,由程序员在代码中明确指定,并在编译时进行类型检查。

pthread_cond_init

pthread_cond_init 是 POSIX 线程(也称为 pthreads)库中用于初始化条件变量的函数。条件变量用于阻塞一个或多个线程,直到另一个线程修改了这些线程等待的共享数据的条件,并通知(或广播)条件变量上的等待线程。这使得线程能够在特定条件成立时继续执行,从而有效地协调线程之间的同步。

_threads.emplace_back

在C++中,_threads.emplace_back 是一个用于向 std::vectorstd::thread(假设 _threads 是这种类型的对象)中直接构造并添加新元素的高效方法。这个方法属于 std::vector 的成员函数之一,特别是针对存储 std::thread 对象的情况特别有用,因为它可以避免不必要的拷贝或移动操作,直接在 vector 的末尾构造新的 std::thread 对象。

std::bind

std::bind 是 C++11 引入的一个函数模板,它位于 头文件中。std::bind 用于生成一个新的可调用实体(如函数对象),这个新的可调用实体将把其某些(或全部)参数绑定到给定的值上,并且它可以接受剩余的(未被绑定的)参数进行调用。这提供了一种灵活的机制来适配函数调用的参数,特别是当你需要将某个函数作为参数传递给另一个函数,但该函数的参数列表不完全匹配时。

std::placeholders

std::placeholders 是 C++ 标准库中的一个命名空间,它定义了一组占位符,这些占位符可以在使用 std::bind 函数时作为参数传递给被绑定的函数或成员函数。这些占位符表示了在 std::bind 返回的新可调用实体被调用时应该提供的参数位置。

std::placeholders 中最常用的是 _1、_2、_3 等,它们分别表示第一个、第二个、第三个等未绑定的参数位置。当 std::bind 返回的可调用实体被调用时,它会将其接收到的参数与这些占位符相匹配,并将这些参数传递给原始的函数或成员函数。

ThreadPool(const ThreadPool &tp) = delete

在C++中,ThreadPool(const ThreadPool &tp) = delete; 这行代码表示你显式地禁止了ThreadPool模板类的拷贝构造函数。这是通过将该构造函数的定义标记为delete来实现的,这是一种特殊的语法,用于明确指出该函数不应被生成或调用。


总结

对于我的笔记就先分享到这里啦
以后还有更多更难的知识点哦
好好珍惜每一点的学习时光哟
大家继续加油吧!

相关推荐
轩辰~13 分钟前
网络协议入门
linux·服务器·开发语言·网络·arm开发·c++·网络协议
lxyzcm33 分钟前
C++23新特性解析:[[assume]]属性
java·c++·spring boot·c++23
蜀黍@猿1 小时前
C/C++基础错题归纳
c++
雨中rain1 小时前
Linux -- 从抢票逻辑理解线程互斥
linux·运维·c++
oneouto1 小时前
selenium学习笔记(二)
笔记·学习·selenium
sealaugh321 小时前
aws(学习笔记第十九课) 使用ECS和Fargate进行容器开发
笔记·学习·aws
Bessssss1 小时前
centos日志管理,xiao整理
linux·运维·centos
s_yellowfish1 小时前
Linux服务器pm2 运行chatgpt-on-wechat,搭建微信群ai机器人
linux·服务器·chatgpt
豆是浪个1 小时前
Linux(Centos 7.6)yum源配置
linux·运维·centos
vvw&1 小时前
如何在 Ubuntu 22.04 上安装 Ansible 教程
linux·运维·服务器·ubuntu·开源·ansible·devops