同步:完成这一步才能进行下一步。异步:不等待,完成后再回调处理。阻塞:调用者被迫等待,非阻塞:调用者不必等待。

146. C++11 中的 std::future 和 std::async 用法?

std::future 和 std::async 是 C++11 引入的线程支持库的一部分,主要用于 异步操作 和 线程结果的获取,大大简化了多线程编程的复杂性。

在这里插入图片描述

使用方法

#include <iostream>
#include <future>
#include <thread>

// 一个耗时计算函数
int slowFunction(int x) {
    std::this_thread::sleep_for(std::chrono::seconds(3)); // 模拟耗时操作
    return x * x;
}

int main() {
    // 使用 std::async 启动异步任务
    /*
    std::async:是 C++11 中引入的异步任务启动机制,用来启动一个异步任务,返回一个 std::future 对象。这个任务会在后台线程执行。

    std::launch::async:表示异步启动,即强制在新的线程中执行 slowFunction。这意味着 slowFunction 在主线程外的另一个线程中运行。

    slowFunction:需要被异步执行的函数。

    10:是传递给 slowFunction 的参数,slowFunction(10) 会被调用,返回 10 的平方。
    */
    std::future<int> result = std::async(std::launch::async, slowFunction, 10);

    std::cout << "Doing other work while waiting for the result...\n";

    // 获取异步任务的结果(阻塞,直到任务完成)
    // std::future 提供了 get() 方法来获取异步任务的结果。如果任务还没有完成,调用 get() 会阻塞当前线程,直到任务完成并返回结果
    int value = result.get();

    std::cout << "Result is: " << value << std::endl;

    return 0;
}

在这里插入图片描述

std::async 的执行策略

在这里插入图片描述

#include <iostream>
#include <future>

int compute(int x) {
    std::cout << "Computing...\n";
    return x * 2;
}

int main() {
    // 异步策略
    std::future<int> asyncResult = std::async(std::launch::async, compute, 10);

    // 延迟策略
    /*
    std::launch::deferred:表示采用延迟执行策略。这个策略意味着 compute(20) 函数不会立即在后台线程执行,而是当调用 deferredResult.get() 时,任务才会在主线程中执行。换句话说,延迟策略不会在异步任务启动时就开始计算,而是等到请求结果时才执行计算。

    compute:指定要延迟执行的函数。

    20:传递给 compute 函数的参数,这里是 20
    */
    std::future<int> deferredResult = std::async(std::launch::deferred, compute, 20);

    std::cout << "Other work is being done...\n";

    // 获取异步策略结果(任务已在后台运行)
    std::cout << "Async result: " << asyncResult.get() << std::endl;

    // 获取延迟策略结果(此时才运行任务)
    std::cout << "Deferred result: " << deferredResult.get() << std::endl;

    return 0;
}

在这里插入图片描述

146.3 结合 std::promise与 std::future

  • std::promise 用于设置共享状态的值
  • std::future 用于获取共享状态的值
#include <iostream>
#include <future>
#include <thread>

void computeSquare(std::promise<int>&& prom, int x) {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    prom.set_value(x * x); // 设置结果
}

int main() {
    std::promise<int> prom;               // 创建 promise (它就像是倒斗中的斗 摸金校尉的工具)
    std::future<int> fut = prom.get_future(); // 获取 future

    std::thread t(computeSquare, std::move(prom), 10);

    std::cout << "Waiting for the result...\n";

    int result = fut.get(); // 获取异步结果(阻塞)
    std::cout << "Result is: " << result << std::endl;

    t.join();

    return 0;
}

在这里插入图片描述

注意事项

在这里插入图片描述

147. std::unique_lock与std::lock_guard的区别是什么?

在这里插入图片描述

功能对比

在这里插入图片描述

std::lock_guard

在这里插入图片描述

#include <iostream>
#include <mutex>
#include <thread>

std::mutex mtx;

void safeFunction() {
    std::lock_guard<std::mutex> guard(mtx); // 自动锁定
    std::cout << "Thread-safe operation\n";
    // 离开作用域时自动解锁
}

int main() {
    std::thread t1(safeFunction);
    std::thread t2(safeFunction);
    t1.join();
    t2.join();
    return 0;
}

std::unique_lock

  • 提供灵活的锁管理功能。
  • 支持延迟锁定、尝试锁定和超时锁定。
  • 可以显式解锁(unlock())或重新锁定(lock())。

(1)延迟锁定

#include <iostream>
#include <mutex>
#include <thread>

std::mutex mtx;

void safeFunction() {
    std::unique_lock<std::mutex> lock(mtx, std::defer_lock); // 延迟锁定
    
    //
    // 执行一些操作,不需要锁
    lock.lock(); // 显式锁定
    std::cout << "Thread-safe operation\n";
    // 离开作用域时自动解锁
}

int main() {
    std::thread t1(safeFunction);
    std::thread t2(safeFunction);
    t1.join();
    t2.join();
    return 0;
}

(2)尝试锁定

#include <iostream>
#include <mutex>
#include <thread>

std::mutex mtx;

void safeFunction() {
    /*
    std::try_to_lock 表示我们不等待锁定,而是尝试锁定,如果当前锁已经被其他线程锁住了,它会立即返回并不会阻塞当前线程。

    如果锁定成功,lock 会管理对 mtx 的持有。

    如果锁定失败,lock 将不会持有 mtx,并且会进入 else 分支。

    if (lock.owns_lock()):检查 lock 是否成功持有锁。
    */
    std::unique_lock<std::mutex> lock(mtx, std::try_to_lock); // 尝试锁定
    if (lock.owns_lock()) {
        std::cout << "Acquired the lock\n";
    } else {
        std::cout << "Failed to acquire the lock\n";
    }
}

int main() {
    std::thread t1(safeFunction);
    std::thread t2(safeFunction);
    t1.join();
    t2.join();
    return 0;
}

在这里插入图片描述

(3)显式解锁和重新锁定

#include <iostream>
#include <mutex>
#include <thread>

std::mutex mtx;

void safeFunction() {
    std::unique_lock<std::mutex> lock(mtx); // 自动锁定
    std::cout << "Critical section\n";
    lock.unlock(); // 手动解锁
    // 非临界区操作
    lock.lock();   // 手动重新锁定
    std::cout << "Critical section again\n";
}

int main() {
    std::thread t1(safeFunction);
    std::thread t2(safeFunction);
    t1.join();
    t2.join();
    return 0;
}

148. static_cast和dynamic_cast区别?

在这里插入图片描述
在这里插入图片描述

static_cast 示例

#include <iostream>
class Base {};
class Derived : public Base {};

int main() {
    Base* base = new Derived();
    // 不安全的类型转换:无运行时检查
    Derived* derived = static_cast<Derived*>(base); 
    if (derived) {
        std::cout << "static_cast 成功\n";
    }
    delete base;
    return 0;
}

dynamic_cast 示例

#include <iostream>
class Base {
public:
    virtual ~Base() {} // 必须是多态类
};
class Derived : public Base {};
class AnotherDerived : public Base {};

int main() {
    Base* base = new AnotherDerived();
    // 安全的类型转换:有运行时检查
    Derived* derived = dynamic_cast<Derived*>(base); 
    if (derived) {
        std::cout << "dynamic_cast 成功\n";
    } else {
        std::cout << "dynamic_cast 失败\n";
    }
    delete base;
    return 0;
}

149. C++中如何实现一个简单的命令模式的示例?

在这里插入图片描述
假设你有一个远程控制器,它不直接控制具体的设备(比如电灯),而是触发一个命令。不同的遥控器可能控制不同的设备,比如电视、空调或电灯。如果我们使用命令模式,那么每个设备的开关操作都会封装为一个命令对象,遥控器只需要知道如何“执行命令(只管发命令,你使劲儿按遥控器,具体能不能打开电视,那是电视的事儿)”,而不关心具体设备的实现。这就是命令模式的精髓所在。

代码示例

可以看我的文章,里面的代码结构更好深入浅出设计模式——行为型模式之命令模式 Command

#include <iostream>
using namespace std;

// Command接口
class Command {
public:
    virtual ~Command() {}
    virtual void execute() = 0;  // 执行命令
};

// 电灯类(接收者  这货才真正负责执行)
class Light {
public:
    void turnOn() {
        cout << "The light is on." << endl;
    }

    void turnOff() {
        cout << "The light is off." << endl;
    }
};

// 开灯命令(对命令进行定义,命令定义中唤醒执行者)
class LightOnCommand : public Command {
private:
    Light* light;  // 这里持有一个Light对象的指针

public:
    LightOnCommand(Light* l) : light(l) {}

    void execute() override {
        light->turnOn();  // 调用Light类的turnOn方法
    }
};

// 关灯命令
class LightOffCommand : public Command {
private:
    Light* light;  // 这里持有一个Light对象的指针

public:
    LightOffCommand(Light* l) : light(l) {}

    void execute() override {
        light->turnOff();  // 调用Light类的turnOff方法
    }
};

// 遥控器(Invoker)对命令进行集成,遥控器上可以弄过个命令
class RemoteControl {
private:
    Command* command;

public:
    RemoteControl() : command(nullptr) {}

    void setCommand(Command* cmd) {
        command = cmd;
    }

    void pressButton() {
        if (command) {
            command->execute();  // 执行当前命令
        }
    }
};

int main() {
    // 创建接收者(电灯)
    Light* light = new Light();

    // 创建命令对象,并传入Light对象
    Command* lightOn = new LightOnCommand(light);
    Command* lightOff = new LightOffCommand(light);

    // 创建遥控器
    RemoteControl* remote = new RemoteControl();

    // 按下遥控器按钮,开灯
    remote->setCommand(lightOn);
    remote->pressButton();

    // 按下遥控器按钮,关灯
    remote->setCommand(lightOff);
    remote->pressButton();

    // 清理内存
    delete lightOn;
    delete lightOff;
    delete light;
    delete remote;

    return 0;
}

在这里插入图片描述

150. 对OpenGL 渲染管线了解吗?

渲染管线是指将一个三维场景转换成二维图像的完整流程,它是一条流水线式的处理过程,每个阶段都负责完成特定的任务。通过渲染管线,我们把计算机里的模型、纹理和光照,变成最终显示在屏幕上的图像。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

渲染管线的主要流程

以 OpenGL 为例,现代的渲染管线分为几个主要阶段:

1. 输入阶段(准备数据)

  • 输入顶点数据:提供物体的基本信息,包括点的坐标、颜色、纹理等。
  • 例子:> “我要画一个三角形,它有三个顶点,每个顶点的位置和颜色是这样的。”

2. 顶点着色器阶段

在这里插入图片描述

3. 图元装配与光栅化

在这里插入图片描述

4. 片段着色器阶段

在这里插入图片描述

5. 测试与混合

在这里插入图片描述

6. 输出图像

  • 最终把处理好的像素数据显示在屏幕上。

之后我会持续更新,如果喜欢我的文章,请记得一键三连哦,点赞关注收藏,你的每一个赞每一份关注每一次收藏都将是我前进路上的无限动力 !!!↖(▔▽▔)↗感谢支持!

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐