C++快餐——C++11(3)

news2024/11/27 21:57:24

在这里插入图片描述

应该在脖子上长一个自己的脑袋

文章目录

  • function包装器
  • bind
    • 绑定普通函数
    • 绑定成员函数
    • std::bind优点
  • 线程库
    • thread类
      • 创建线程
      • 线程管理
      • 线程标识
  • 原子性操作库(atomic)
  • lock_guard与unique_lock
      • std::lock_guard
      • std::unique_lock
  • mutex的种类
  • 总结


function包装器

在这里插入图片描述

C++11引入了一个名为 std::function 的标准函数包装器,也可以叫做适配器,它提供了一种通用的方式来包装可调用对象(函数、函数指针、函数对象、Lambda 表达式等),并可以在需要时进行调用。std::function 是一个模板类,定义在 < functional > 头文件中。示例:

#include <iostream>
#include <functional>

// 被包装的函数
void hello() 
{
    std::cout << "Hello, world!" << std::endl;
}

int main() 
{
    // 将 hello 函数包装为 std::function 对象
    std::function<void()> func = hello;
    
    func();

    return 0;
}

在这里插入图片描述

当使用模板编写函数包装器时,每次使用不同的类型进行实例化时,都会生成一个新的函数实例。这可能导致代码膨胀和额外的开销。为了避免这种情况,可以使用类型擦除技术,将不同类型的可调用对象都存储为相同的类型,从而避免实例化多份。

而这其中的一个常见的类型擦除技术是就使用 std::function,它是一个通用的函数包装器。它可以存储不同类型的可调用对象,而无需生成额外的代码实例化。std::function 使用类型擦除来实现通用的函数封装,可以在运行时根据需要进行调用。例如:

void sum(int x, int y) 
{
    std::cout << "result : " << x + y << std::endl;
}

class sum_func
{
public:
    void operator()(int x, int y)
    {
        std::cout << "result : " << x - y << std::endl;
    }
};

int main() 
{
    auto sum_lambda = [](int x, int y)
    {
        std::cout << "result : " << x * y << std::endl;
    };
    
    sum(10, 7);
    sum_func()(20, 7);
    sum_lambda(30, 5);

    return 0;
}

在这里插入图片描述

假设我们在不同的作用域的不同场景中需要使用到这三个函数时,就需要实例化出三份代码,但是如果使用 function 包装器的话,就可以简洁的实例化一份解决问题。如下:

int main() 
{
    auto sum_lambda = [](int x, int y)
    {
        std::cout << "result : " << x * y << std::endl;
    };

    std::function<void(int, int)> func = sum;
    func = sum_func();
    func = sum_lambda;
    func(10, 7);

    return 0;
}

在这里插入图片描述

std::function 有许多优点,使其成为处理可调用对象的强大工具:

优点说明
通用性和灵活性:std::function 是一个通用的函数封装器,可以存储和调用各种类型的可调用对象,包括函数指针、函数对象、lambda 表达式等。这使得它非常适合处理不同类型的可调用对象,并且可以在不改变代码结构的情况下进行切换和替换。
类型安全std::function 在编译时会对类型进行检查,确保存储的可调用对象与指定的函数签名匹配。这提供了一定程度的类型安全性,避免了在运行时出现类型错误的可能性。
简化接口和回调:通过将可调用对象存储为 std::function,可以将它们作为参数传递给其他函数或类,并在需要时进行调用。这简化了接口的设计,并允许用户在特定事件发生时注册回调函数。
延迟绑定:std::function 允许在运行时绑定可调用对象。这意味着可以将函数的选择推迟到程序运行时,根据动态条件来确定要调用的函数。这在需要根据运行时环境或用户输入动态选择函数的情况下非常有用。
函数组合和封装:std::function 具有函数组合和封装的能力。可以将多个 std::function 对象组合成一个新的函数,实现函数的串联调用或并行调用。此外,std::function 还可以用作其他函数的封装器,将函数的行为进行包装和修改。
可替代模板特化在一些情况下,std::function 可以作为替代模板特化的一种方式。使用模板特化可能导致生成多个函数实例,而 std::function 可以将不同类型的可调用对象都存储为相同的类型,避免了代码膨胀和额外实例化的问题。

总而言之,std::function 提供了一种通用、灵活和类型安全的方式来处理可调用对象。它简化了接口设计、回调注册和函数组合等任务,并能够在运行时进行延迟绑定和动态选择函数。通过这些优点,std::function 成为了处理可调用对象的重要工具之一。

bind

在这里插入图片描述

std::bind 是 C++ 标准库中的一个函数模板,用于创建一个可调用对象,它将参数和函数绑定在一起。std::bind 可以用于将函数与其参数进行绑定,或者将成员函数与对象进行绑定,从而生成一个新的可调用对象。std::bind 也是一个函数模板,它就像一个函数包装器,std::bind 的基本语法如下:

std::bind(Function, Arguments...);

其中,Function 是要绑定的函数或成员函数的名称,Arguments… 是要传递给函数的参数。

绑定普通函数

使用bind绑定普通函数代码如下:

#include <iostream>
#include <functional>

void add(int a, int b) 
{
    std::cout << a + b << std::endl;
}

int main() 
{
    auto addFunc = std::bind(add, 10, 20);
    addFunc();  

    return 0;
}

在这个示例中,我们使用 std::bind 将函数 add 与参数 10 和 20 进行绑定,生成了一个新的可调用对象 addFunc。当我们调用 addFunc() 时,实际上调用了绑定的函数 add,并传递了绑定的参数 10 和 20,输出结果为 30。

在这里插入图片描述

如果不想在绑定的时候将参数定死,还可以使用placeholders命名空间中的占位符来让后期指定

在这里插入图片描述
如下代码:

#include <iostream>
#include <functional>

void add(int a, int b) 
{
    std::cout << a + b << std::endl;
}

int main() 
{
    auto addFunc = std::bind(add, placeholders::_1, placeholders::_2);
    addFunc(20, 34);  

    return 0;
}

在这里插入图片描述

绑定成员函数

除了绑定普通函数之外,还可以使用bind来绑定类成员函数,如下:

#include <iostream>
#include <functional>

class MyClass 
{
public:
    void hello(const std::string& message, const std::string& name)
    {
        std::cout << message  << " , " << name << std::endl;
    }
};

int main() 
{
    MyClass obj;
    auto printFunc = std::bind(&MyClass::hello, &obj, placeholders::_1, placeholders::_2);
    printFunc("Hello", "Jack");

    return 0;
}

在这个示例中,我们定义了一个类 MyClass,其中包含一个成员函数 hello。我们使用 std::bind 将成员函数 hello与对象 obj 以及占位符进行绑定,生成了一个新的可调用对象 printFunc。当我们调用 printFunc() 时,实际上调用了绑定的成员函数 hello,并传递了绑定的对象 obj 和替换占位符的参数。

需要注意的是,当绑定成员函数时,必须使用成员函数的指针,并将对象的指针或引用作为第一个参数传递给 std::bind。

在这里插入图片描述

占位符还可以交换,但是不可以跳跃。

在这里插入图片描述

auto printFunc = std::bind(&MyClass::hello, &obj, placeholders::_2, placeholders::_1);,输出结果如下:

在这里插入图片描述

std::bind优点

std::bind 具有以下几个优点:

优点说明
参数绑定:std::bind 允许将函数的部分参数进行绑定,即在创建可调用对象时可以指定部分参数的值,而无需提供所有参数。这使得在后续调用时可以更方便地使用绑定的可调用对象,只需提供未绑定的参数即可。参数绑定提供了一种延迟执行函数的能力,非常有用。
灵活性std::bind 允许将函数与参数绑定在一起,也可以将成员函数与对象进行绑定。这种灵活性使得我们可以在不改变原有函数或成员函数的情况下,创建新的可调用对象,可以根据实际需求进行参数绑定和对象绑定。
函数适配器std::bind 提供了一些函数适配器,例如 std::placeholders::_1、std::placeholders::_2 等,用于占位符参数。这些占位符可以在创建可调用对象时使用,并在后续调用时动态填充实际参数。函数适配器增强了 std::bind 的灵活性和可用性。
代码重用和简化std::bind 可以用于将函数或成员函数与特定的参数或对象进行绑定,生成新的可调用对象。这样可以避免重复编写相似的代码,提高代码的重用性。同时,它简化了接口设计和回调注册的过程,使代码更加简洁和易读。
延迟绑定和动态调用std::bind 允许在运行时进行参数绑定,并生成可调用对象。这使得我们可以根据运行时的条件或需求来动态选择要调用的函数或成员函数。这种延迟绑定和动态调用的能力非常有用,可以提高代码的灵活性和可扩展性。

总而言之,std::bind 提供了一种灵活、可定制和方便的方式来创建可调用对象,并在需要的时候进行参数绑定。它可以用于函数的部分参数绑定、成员函数的对象绑定,以及使用函数适配器进行占位符参数的处理。通过这些优点,std::bind 提供了更多的编程选项和技巧,使代码更加灵活和可重用。

线程库

C++ 线程库是 C++11 标准引入的一个库,提供了一组用于多线程编程的类和函数,旨在简化并发编程的实现。它以 std::thread 为核心,还包括其他类和函数,用于线程管理、线程同步和线程间通信。C++ 线程库的主要组件和功能如下

组件功能
std::thread 类:std::thread 是 C++ 线程库的核心类,用于创建和管理线程。通过创建 std::thread 对象,可以在新的线程中执行指定的函数或可调用对象。它提供了函数来控制线程的启动、加入(join)、分离(detach)和线程标识等操作。
线程同步原语C++ 线程库提供了多种线程同步原语,用于协调不同线程之间的操作。其中包括互斥量(std::mutex)、条件变量(std::condition_variable)、原子操作(std::atomic)等。这些同步原语可以用于实现线程间的互斥访问、条件等待和原子操作,确保线程安全和协同工作。
std::async 和 std::future:std::async 和 std::future 是 C++ 线程库提供的用于异步编程的工具。std::async 可以在后台启动一个异步任务,并返回一个 std::future 对象,用于获取异步任务的结果。std::future 可以用于等待异步任务的完成,并获取其结果,或者通过 std::future 的异步操作来检查任务的状态。
std::atomic 类std::atomic 类是 C++ 线程库提供的用于原子操作的模板类。它提供了原子类型的操作函数,用于实现并发环境下的安全数据访问。通过 std::atomic,可以避免竞争条件和数据竞争,确保多线程环境下的数据一致性。
std::mutex 类:std::mutex 是 C++ 线程库提供的互斥量类,用于实现线程间的互斥访问。它提供了锁和解锁操作,用于保护临界区代码,避免多个线程同时访问共享资源导致的竞争条件和数据竞争。
std::condition_variable 类std::condition_variable 是 C++ 线程库提供的条件变量类,用于线程间的条件等待和通知。它配合互斥量使用,可以实现线程的等待和唤醒机制,用于线程间的同步和协调。

C++ 线程库提供了丰富的功能和工具,使得多线程编程更加方便和安全。它简化了线程的创建、管理和同步,提供了一组统一的接口和工具,用于处理多线程环境下的并发操作。通过这些组件,开发人员可以更轻松地实现并发算法、多线程任务和并行计算等应用。

thread类

在这里插入图片描述

std::thread 是 C++ 标准库中提供的类,用于创建和管理线程。它是 C++ 线程库的核心组件之一,位于 < thread > 头文件中。使用 std::thread,可以在新的线程中执行指定的函数或可调用对象。

创建线程

可以通过 std::thread 的构造函数创建线程。构造函数接受一个可调用对象(函数指针、函数对象或 lambda 表达式等)作为参数,并在新的线程中执行该可调用对象。还可以通过在 std::thread 构造函数中传递参数,将参数传递给线程的执行函数。
在这里插入图片描述

void threadFunction(int value) 
{
    // 线程执行的代码,可以使用传递的参数
}

int main() 
{
    int data = 42;
    std::thread t(threadFunction, data);  // 传递参数给线程函数
    // ...
    t.join();  // 等待线程结束
    return 0;
}

线程管理

std::thread 提供了一些函数来管理线程的行为。其中包括 join() 和 detach() 函数。join() 函数用于等待线程执行完毕。调用 join() 后,主线程将被阻塞,直到被调用的线程完成执行。
在这里插入图片描述

void threadFunction() 
{
    // 线程执行的代码
}

int main() 
{
    std::thread t(threadFunction);  // 创建线程并指定可调用对象
    // ...
    t.join();  // 等待线程结束
    return 0;
}

detach() 函数用于分离线程。调用 detach() 后,线程将在后台继续执行,主线程不再等待该线程的结束。detach后就不可以join了
在这里插入图片描述

void threadFunction() 
{
    // 线程执行的代码
}

int main() 
{
    std::thread t(threadFunction);  // 创建线程
    // ...
    t.detach();  // 分离线程,主线程不再等待
    return 0;
}

线程标识

std::thread 对象具有一个唯一的线程标识,可以通过 std::thread::id 类型的成员函数 get_id() 获取线程的标识。
在这里插入图片描述

void threadFunction() {
    // 线程执行的代码
}

int main() {
    std::thread t(threadFunction);  // 创建线程
    std::thread::id threadId = t.get_id();  // 获取线程标识
    // ...
    t.join();  // 等待线程结束
    return 0;
}

需要注意的是,当 std::thread 对象被销毁时,如果线程仍在运行且未被分离(未调用 detach()),则会导致程序终止。因此,确保在线程结束之前调用 join() 或 detach() 来管理线程的生命周期。std::thread 还提供了其他函数和操作,例如 native_handle() 函数用于获取底层操作系统的线程句柄,hardware_concurrency() 函数用于获取系统支持的并发线程数量等。且线程函数的参数是以值拷贝的方式拷贝到线程栈空间中的,因此即使线程参数为引用类型,在
线程中修改后也不会修改外部实参,因为其实际引用的是线程栈中的拷贝,而不是外部实参。

通过 std::thread,可以方便地创建和管理线程,实现并发编程。它为多线程操作提供了一个高级的 C++ 接口,简化了线程的创建、管理和同步,提高了并发编程的可读性和可维护性。

原子性操作库(atomic)

在这里插入图片描述

当程序涉及多线程时,随之而来的就是线程安全问题,当多个线程对同一个数据进行修改时,非常容易引发线程安全问题,在C++11之前的解决方法通常是使用加锁和解锁方法,但是在加锁和解锁这段过程之间只能有一个线程进行访问,其他线程都会被阻塞在外面,影响程序运行的效率,而且锁如果控制不好,还容易造成死锁。因此C++11中引入了原子操作,即不可被中断的一个或一系列操作,C++11引入的原子操作类型,使得线程间数据的同步变得非常高效。

C++ 提供的用于原子操作的 std::atomic 库位于 头文件中。std::atomic 提供了一组模板类和函数,用于定义和操作原子类型的对象。以下是 std::atomic 库的主要特点和使用方法:

  • 原子类型: std::atomic 支持多种原子类型,如整数类型(int、unsigned int、std::size_t 等)、指针类型和布尔类型等。可以使用 std::atomic 来定义原子类型的对象,其中 T 是要原子化的数据类型。
std::atomic<int> counter(0);  // 定义一个原子整数对象
  • 原子操作: std::atomic 提供了一组原子操作函数,用于对原子对象进行读取、写入和修改等操作。这些操作函数保证在多线程环境中的原子性,避免了竞争条件和数据竞争。
    load():原子读取操作,获取原子对象的当前值。
    store():原子写入操作,修改原子对象的值。
    exchange():原子交换操作,将新值赋予原子对象,并返回原来的值。
    fetch_add() 和 fetch_sub():原子加法和减法操作,将指定值加到原子对象上或从原子对象中减去指定值,并返回原子对象原来的值。
std::atomic<int> counter(0);
int value = counter.load();  // 原子读取操作
counter.store(10);  // 原子写入操作
int oldValue = counter.exchange(5);  // 原子交换操作
int result = counter.fetch_add(3);  // 原子加法操作
  • 原子性操作: std::atomic 提供了原子性操作的保证,确保多个线程对同一个原子对象的操作是互斥的。这意味着在原子操作期间,不会发生数据竞争。

  • 内存顺序: std::atomic 还支持指定内存顺序,用于控制原子操作的内存可见性和排序方式。可以使用 std::memory_order 枚举类型来指定内存顺序,如 std::memory_order_relaxed、std::memory_order_acquire、std::memory_order_release 等。

std::atomic<int> counter(0);

counter.fetch_add(1, std::memory_order_relaxed);  // 松散的内存顺序
counter.fetch_add(1, std::memory_order_acquire);  // 获取顺序
counter.fetch_add(1, std::memory_order_release);  // 释放顺序

通过 std::atomic,可以实现线程安全的原子操作,避免竞争条件和数据竞争,确保多线程环境下的数据一致性。它为并发编程提供了一种高效且可靠的方式,用于处理共享数据的并发访问和修改。

lock_guard与unique_lock

std::lock_guard 和 std::unique_lock 是 C++ 标准库中用于提供互斥量(mutex)的 RAII封装类。它们用于管理互斥量的加锁和解锁操作,确保在作用域结束时自动释放锁,避免忘记手动解锁而导致的死锁或数据竞争。

std::lock_guard

在这里插入图片描述

std::lock_guard 是一个模板类,通过在构造函数中传入互斥量(std::mutex 或其派生类)对象,可以自动加锁,而在析构函数中自动解锁。它适用于简单的加锁和解锁操作,没有额外的灵活性。

std::mutex mtx;

void foo()
{
    std::lock_guard<std::mutex> lock(mtx); // 自动加锁
    // 执行受保护的代码
} // 自动解锁

std::unique_lock

在这里插入图片描述

std::unique_lock 也是一个模板类,与 std::lock_guard 类似,可以管理互斥量的加锁和解锁操作。不同之处在于 std::unique_lock 提供了更多的灵活性和可配置性。可以在构造函数中传入互斥量对象,并指定锁定类型和内存顺序。

std::mutex mtx;

void foo()
{
    std::unique_lock<std::mutex> lock(mtx); // 自动加锁
    // 执行受保护的代码
    lock.unlock(); // 手动解锁
    // 执行其他操作
    lock.lock(); // 再次加锁
    // 执行受保护的代码
} // 自动解锁

std::unique_lock 还提供了以下特性:

  • 支持延迟加锁和条件变量:可以在不加锁的情况下创建 std::unique_lock 对象,并在需要时手动加锁。
  • 支持对互斥量的所有权转移:可以将 std::unique_lock 对象的所有权从一个线程转移到另一个线程,实现更灵活的锁管理。

总的来说,std::lock_guard 简单且直观,适用于大多数情况下的互斥量管理。而 std::unique_lock 提供了更多的灵活性和可配置性,适用于需要更复杂锁定策略和条件变量的情况。选择使用哪个类取决于具体需求和使用场景。

mutex的种类

C++ 标准库中提供了几种互斥量(mutex)的类型,用于实现线程间的互斥访问和同步。下面是常见的互斥量类型:

类型说明
std::mutexstd::mutex 是最基本的互斥量类型,提供了最基本的加锁和解锁操作。它是非递归的互斥量,意味着同一个线程无法多次对同一个互斥量进行加锁,否则会导致死锁。多个线程可以通过 std::mutex 实现对共享资源的互斥访问。
std::recursive_mutexstd::recursive_mutex 是递归互斥量,允许同一个线程多次对互斥量进行加锁。递归互斥量会跟踪每个线程对互斥量的加锁次数,在解锁时需要相应数量的解锁操作。递归互斥量适用于需要递归调用的情况,但需要注意避免死锁。
std::timed_mutexstd::timed_mutex 是具有超时功能的互斥量。它提供了 try_lock_for() 和 try_lock_until() 成员函数,可以尝试在一段时间内加锁,如果加锁失败则返回。这对于需要在一定时间内获取锁的场景非常有用。
std::recursive_timed_mutexstd::recursive_timed_mutex 是具有超时功能的递归互斥量。它结合了 std::recursive_mutex 和 std::timed_mutex 的特性,可以在一定时间内递归地尝试加锁。
std::shared_mutexstd::shared_mutex 是读写锁(shared_mutex),也称为共享互斥量。它支持共享读取和独占写入模式。多个线程可以同时获取读取锁,但只有一个线程可以获取写入锁。读取锁可以被多个线程同时持有,写入锁则独占。这对于读多写少的场景可以提供更好的性能。

这些互斥量类型都定义在 头文件中,可以通过它们来实现线程之间的互斥访问和同步。选择合适的互斥量类型取决于具体的应用场景和需求。

总结

文章介绍了C++11中的包装器以及bind函数模板,还对C++11中添加的线程库进行介绍,并对多线程操作产生的线程安全问题进行分析解决,引入了原子操作库以及互斥量mutex。这些知识点对于帮助我们写出高质量C++程序的用处极大,如果文章中有哪些内容能够帮助到你的话也算是文章有其所存在的价值,劳烦点个赞就当是对博主的肯定呗!

在这里插入图片描述

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

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

相关文章

ubuntu启动报错error: proc_thermal_add, will cont

如题&#xff0c;ubuntu启动报错error: proc_thermal_add, will cont 截图如下&#xff1a; 困扰了我很久&#xff0c;差点就打算重装系统&#xff0c;准备放弃了&#xff0c;但是感谢国外的老哥&#xff0c;写了一篇非常详细的解决方案&#xff0c;我搬过来。 解决方案&#…

基础课14——文本标注

人工智能界流传一句俏皮话&#xff1a;“有多少人工&#xff0c;就有多少智能。” 1.人工智能数据标注介绍 2018年9月&#xff0c;百度智能云与山西综改示范区达成合作&#xff0c;在太原共同建立了百度&#xff08;山西&#xff09;人工智能基础数据产业基地&#xff08;简称…

AI时代,ChatGPT与文心一言选哪一个?

&#x1f388;个人公众号:&#x1f388; :✨✨✨ 可为编程✨ &#x1f35f;&#x1f35f; &#x1f511;个人信条:&#x1f511; 为与不为皆为可为&#x1f335; 你们平时都是在什么情况下使用GPT的呢&#xff1f;为何使用&#xff1f;都使用什么平台的&#xff1f; 针对以上问…

高防CDN的特点与作用

高级防护CDN是一种专门设计用于提供出色的网络安全功能的内容分发网络。它通过采用分布式节点、智能路由和强大的安全机制&#xff0c;旨在确保网站在面临各种网络攻击时保持安全&#xff0c;同时提供内容的快速传递和优化功能。那么&#xff0c;高级防护CDN有哪些独特的特点和…

win10pycharm和anaconda安装和环境配置教程

windows10 64位操作系统下系统运行环境安装配置说明 下载和安装Anaconda&#xff0c;链接https://www.anaconda.com/download 下载完后&#xff0c;双击exe文件 将anaconda自动弹出的窗口全部关掉即可&#xff0c;然后配置高级系统变量 根据自己的路径&#xff0c;配置…

前端移动高级web详细解析五

响应式布局方案 媒体查询 Bootstrap框架 01-媒体查询 基本写法 max-width&#xff1a;最大宽度&#xff08;小于等于&#xff09; min-width&#xff1a;最小宽度&#xff08;大于等于&#xff09; 书写顺序 min-width&#xff08;从小到大&#xff09; max-width&…

IDEA上也能用Postman了?

Postman是大家最常用的API调试工具&#xff0c;国产API调试工具 Apipost 推出IDEA插件&#xff0c;写完代码就可以调试接口并一键生成接口文档&#xff01;而且还可以根据已有的方法帮助您快速生成 url 和 params。Apipost Helper API 调试工具 API 管理工具 API 搜索工具。…

领先实践|IDEO 最佳设计思维和策略框架

设计思维是一种以人为本的创新方法&#xff0c;它从人类的角度出发&#xff0c;考虑技术上可行和经济上可行的内容。框架可以成为实现设计思维、策略和系统设计的有用工具。本文由此展开阐述 IDEO 的最佳设计思维和策略框架。 01. 设计思维框架 1.1 设计思维过程 设计思维?是…

【python】爬取豆瓣电影排行榜TOP250存储到CSV文件中

一、导入必要的模块&#xff1a; 代码首先导入了需要使用的模块&#xff1a;requests、lxml和csv。 import requests from lxml import etree import csv 如果出现模块报错 进入控制台输入&#xff1a;建议使用国内镜像源 pip install 模块名称 -i https://mirrors.aliyun.co…

Failed to launch task: 文件”Setup”不存在 Mac安装Adobe软件报错解决方案

在安装 Adobe 软件时&#xff0c;软件提示 Failed to launch task: 文件”Setup”不存在 &#xff0c;这个时候怎么处理呢&#xff1f; 解决方法如下&#xff1a; 1、安装 AnitCC 或 或 Creative Cloud 环境&#xff0c;保证软件所需要的环境 2、如果安装后也不起作用&#x…

[ThinkPHP]源码阅读:Model的获取器

目录 1、ThinkPHP组件版本 2、业务Model代码 3、阅读框架源码 4、跳过获取器获取原始数据写法 1、ThinkPHP组件版本 topthink/think-orm v2.0.58 topthink/think-helper v3.1.6 2、业务Model代码 原理&#xff1a;Model通过调用toArray方法使用自定义的获取器 3、阅读框架…

GZ035 5G组网与运维赛题第8套

2023年全国职业院校技能大赛 GZ035 5G组网与运维赛项&#xff08;高职组&#xff09; 赛题第8套 一、竞赛须知 1.竞赛内容分布 竞赛模块1--5G公共网络规划部署与开通&#xff08;35分&#xff09; 子任务1&#xff1a;5G公共网络部署与调试&#xff08;15分&#xff09; 子…

Web Woeker和Shared Worker的使用以及案例

文章目录 1、前言2、介绍 Web Worker3、使用须知及兼容性3.1、使用须知3.2、兼容性 4、使用 Web Worker4.1、创建 Web Worker4.2、与主线程通信4.3、终止 Web Worker4.4、监听错误信息 5、使用 Shared Worker4.5、调试 Shared Worker 6、使用中的一些坑6.1、Web Woeker 中引入了…

第4章_运算符

文章目录 1. 算术运算符1.1 加法与减法运算符1.2 乘法与除法运算符1.3 求模运算符 2. 比较运算符2.1 等号运算符2.2 安全等于运算符2.3 不等于运算符2.4 空运算符2.5 非空运算符2.6 最小值运算符2.7 最大值运算符2.8 BETWEEN AND运算符2.9 IN运算符2.10 NOT IN运算符2.11 LIKE运…

k8s中kubectl命令式对象、命令式对象配置、声明式对象配置管理资源介绍

目录 一.kubernetes资源管理简介 二.三种资源管理方式优缺点比较 三.命令式对象管理介绍 1.kubectl命令语法格式 2.资源类型 &#xff08;1&#xff09;通过“kubectl api-resources”来查看所有的资源 &#xff08;2&#xff09;每列含义 &#xff08;3&#xff09;常…

Java 设计模式——命令模式

目录 1.概述2.结构3.案例实现3.1.命令接口3.2.具体命令3.3.接受者3.4.调用者3.5.测试 4.优缺点5.使用场景6.JDK 源码解析——Runnable 1.概述 &#xff08;1&#xff09;日常生活中&#xff0c;我们出去吃饭都会遇到下面的场景&#xff1a; &#xff08;2&#xff09;命令模…

Go语言集成开发环境(IDE):GoLand 2023中文

GoLand 2023是一款由JetBrains开发的现代化、功能丰富的Go语言集成开发环境&#xff08;IDE&#xff09;。它提供了智能代码提示和自动完成、强大的内置调试器以及代码重构工具&#xff0c;帮助开发者提高编码效率并确保代码质量。GoLand 2023还支持多种版本控制系统&#xff0…

kubernetes-ingress-nginx

目录 一、部署 二、访问 1.基于路径访问 2.基于域名访问 三、加密与认证 1.TLS加密 2.auth认证 四、rewrite重定向 五、canary金丝雀发布 1.基于header灰度 2.基于权重灰度 3.业务域拆分 一、部署 ingress-nginx是一个开源的Kubernetes Ingress控制器&#xff0c;用…

真实经历分享:性能测试需求分析详解

在实际的工作中系统的性能需求通常是一个笼统的需求&#xff0c;而且有可能给提需求的人并不知道具体的性能需要&#xff0c;所以只能含糊的列出。如果测试人员不搞清楚&#xff0c;就会出现实际要把杀猪刀&#xff0c;需求标明能屠龙&#xff01;&#xff01;&#xff01; 下面…

C++二叉搜索树模拟实现

目录 一、二叉搜索树的概念 二、二叉搜索树的结构 三、二叉搜索树的操作&#xff08;非递归&#xff09; 1.插入 2.查找 3.删除 4.遍历 四、二叉搜索树的操作&#xff08;递归&#xff09; 1.递归插入 2.递归查找 3.递归删除 4.递归遍历 五、二叉搜索树的默认成员函…