当前位置: 首页 > news >正文

Std::Future大冒险:穿越C++并发宇宙的时空胶囊

序章:一场并发的时空之旅

欢迎来到C++并发宇宙的奇妙世界!在这里,代码不再是一条孤独的直线,而是化身为万千时间线交织的绚烂织锦。想象一下,你是一位太空任务指挥官,需要同时监控多个深空探测器。每个探测器都在亿万公里之外独立工作,但你却需要它们最终将珍贵的数据带回地球。

如果没有合适的工具,这将是噩梦般的任务:不断轮询每个探测器"数据准备好了吗?",浪费宝贵的计算资源,还可能错过重要信息。但幸运的是,C++11为我们带来了std::future——就像是为每个探测器配备了一个智能数据胶囊,它会在数据到达时自动通知我们:“指挥官,您要的数据已送达!”

这就是std::future的魅力:它让我们从"不断询问"的忙碌等待模式,升级到"数据送达,请签收"的优雅通知模式。现在,让我们开始这场关于未来(future)的探险吧!

第一章:并发编程的"前世今生"

1.1 远古时代:多线程的原始呼唤

std::future诞生之前,C++程序员们如何实现异步操作呢?让我们回顾一下那段"艰苦岁月":

// 旧时代的异步任务处理
#include <thread>
#include <iostream>
#include <mutex>
#include <condition_variable>class AncientAsyncTask {
private:std::thread workerThread;int result;bool ready = false;std::mutex mtx;std::condition_variable cv;public:void startCalculation() {workerThread = std::thread([this]() {// 模拟耗时计算std::this_thread::sleep_for(std::chrono::seconds(2));std::unique_lock<std::mutex> lock(mtx);result = 42; // 著名的宇宙终极答案ready = true;cv.notify_one(); // 通知等待的线程});}int getResult() {std::unique_lock<std::mutex> lock(mtx);cv.wait(lock, [this]() { return ready; }); // 等待结果准备好return result;}~AncientAsyncTask() {if (workerThread.joinable()) {workerThread.join();}}
};

这种方式虽然可行,但存在几个问题:

  1. 代码冗长:需要手动管理线程、互斥锁、条件变量
  2. 容易出错:死锁、竞态条件等问题防不胜防
  3. 资源管理复杂:需要确保线程正确join或detach
  4. 异常处理困难:线程中的异常很难传递回主线程

1.2 新时代的黎明:Std::Future的诞生

C++11引入了std::future,让异步编程变得前所未有的简洁:

#include <future>
#include <iostream>int computeTheAnswer() {std::this_thread::sleep_for(std::chrono::seconds(2));return 42;
}int main() {// 启动异步任务std::future<int> answerFuture = std::async(computeTheAnswer);// 在等待的同时可以做其他工作std::cout << "思考生命、宇宙及万事万物..." << std::endl;// 当需要结果时,等待并获取它int answer = answerFuture.get();std::cout << "宇宙的终极答案是: " << answer << std::endl;return 0;
}

看!代码从20多行缩减到不到10行,而且更加清晰和安全。这就是std::future带来的革命性变化。

第二章:Std::Future核心概念解析

2.1 什么是Future?

std::future是一个类模板,它代表一个尚未完成但将来会完成的异步操作的结果。可以把std::future想象成一张提货单或者快递单:

  • 现在:你有一张提货单(future),但货物还没到
  • 将来:你可以用这张提货单获取货物(结果)
std::future<int> deliveryTicket = std::async([]() {// 模拟货物运输过程std::this_thread::sleep_for(std::chrono::seconds(3));return 1001; // 货物编号
});// 此时货物还在运输中,但我们已经有了提货单// ... 可以做其他事情 ...int goodsNumber = deliveryTicket.get(); // 凭提货单取货

2.2 Future的状态机

每个std::future对象都有一个明确的状态生命周期:

初始状态
Deferred/Started
运行中
Working
已完成
Ready
超时/异常
Ready
  • Deferred/Started:异步操作已创建但可能尚未开始
  • Working:操作正在进行中
  • Ready:操作已完成,结果可用
  • Empty:结果已被获取,future不再有效

2.3 Future的家族成员

std::future不是孤军奋战,它有一个强大的并发家族:

类型描述使用场景
std::future<T>基本future类型,独占结果单个消费者
std::shared_future<T>可共享的future,允许多次获取多个消费者
std::promise<T>提供设置结果的能力生产者端
std::packaged_task<T>将函数包装为异步任务任务包装
std::async启动异步任务的便捷方式快速启动

第三章:Std::Future的实战应用

3.1 基本用法:Std::Async的魔法

std::async是使用std::future最简单的方式,它有两种启动策略:

#include <future>
#include <iostream>
#include <chrono>int calculateMeaningOfLife() {std::this_thread::sleep_for(std::chrono::seconds(2));return 42;
}void basicAsyncUsage() {// 方式1: 异步执行(默认)auto future1 = std::async(calculateMeaningOfLife);// 方式2: 明确指定异步执行auto future2 = std::async(std::launch::async, calculateMeaningOfLife);// 方式3: 延迟执行(直到调用get()时才执行)auto future3 = std::async(std::launch::deferred, calculateMeaningOfLife);std::cout << "正在执行其他任务..." << std::endl;// 获取结果(如果是deferred,此时才开始执行)std::cout << "结果: " << future1.get() << std::endl;std::cout << "结果: " << future2.get() << std::endl;std::cout << "结果: " << future3.get() << std::endl;
}

3.2 异常处理:Future的错误传递机制

std::future的强大之处在于它能无缝传递异常:

void mightThrowException() {throw std::runtime_error("异步任务中发生了错误!");
}void exceptionHandling() {try {auto future = std::async(mightThrowException);future.get(); // 这里会抛出异常} catch (const std::exception& e) {std::cerr << "捕获到异常: " << e.what() << std::endl;}
}

3.3 超时控制:等待的艺术

std::future提供了灵活的等待机制:

void timeoutExample() {auto slowTask = []() {std::this_thread::sleep_for(std::chrono::seconds(5));return "任务完成";};auto future = std::async(slowTask);// 方式1: 等待指定时间auto status = future.wait_for(std::chrono::seconds(2));if (status == std::future_status::ready) {std::cout << "任务已完成: " << future.get() << std::endl;} else if (status == std::future_status::timeout) {std::cout << "任务超时,仍在进行中..." << std::endl;}// 方式2: 等待直到指定时间点auto deadline = std::chrono::system_clock::now() + std::chrono::seconds(3);status = future.wait_until(deadline);// 最终获取结果(如果需要)if (status == std::future_status::ready) {std::cout << "最终结果: " << future.get() << std::endl;}
}

第四章:高级特性与模式

4.1 Std::Promise:亲手打造Future

std::promise允许我们手动创建和设置future的结果:

void promiseExample() {// 创建promise和对应的futurestd::promise<std::string> promise;std::future<std::string> future = promise.get_future();// 在另一个线程中设置结果std::thread worker([&promise]() {std::this_thread::sleep_for(std::chrono::seconds(2));promise.set_value("Hello from promise!");});// 等待并获取结果std::cout << "等待结果..." << std::endl;std::cout << future.get() << std::endl;worker.join();
}

4.2 Std::Packaged_Task:函数的未来包装

std::packaged_task将函数和future绑定在一起:

void packagedTaskExample() {// 创建一个packaged_task,包装一个函数std::packaged_task<int(int, int)> task([](int a, int b) {return a + b;});// 获取与任务关联的futurestd::future<int> result = task.get_future();// 在另一个线程中执行任务std::thread worker(std::move(task), 10, 20);// 获取结果std::cout << "10 + 20 = " << result.get() << std::endl;worker.join();
}

4.3 Shared_Future:结果的共享之旅

std::shared_future允许多个消费者获取同一个结果:

void sharedFutureExample() {auto task = []() {std::this_thread::sleep_for(std::chrono::seconds(1));return 3.14159; // 返回π};// 创建普通的futurestd::future<double> future = std::async(task);// 转换为shared_future(可以复制)std::shared_future<double> shared_future = future.share();// 多个线程可以共享访问同一个结果auto printer = [](std::shared_future<double> f, int id) {std::cout << "线程" << id << "获取结果: " << f.get() << std::endl;};std::thread t1(printer, shared_future, 1);std::thread t2(printer, shared_future, 2);std::thread t3(printer, shared_future, 3);t1.join();t2.join();t3.join();
}

第五章:实战案例精选

5.1 案例一:并行数据处理管道

让我们构建一个并行的数据处理管道,展示std::future在复杂场景中的应用:

#include <future>
#include <vector>
#include <iostream>
#include <random>
#include <algorithm>class DataProcessingPipeline {
private:// 阶段1: 生成数据static std::vector<int> generateData(int count) {std::vector<int> data;data.reserve(count);std::random_device rd;std::mt19937 gen(rd());std::uniform_int_distribution<> dis(1, 100);for (int i = 0; i < count; ++i) {data.push_back(dis(gen));}return data;}// 阶段2: 处理数据static std::vector<int> processData(const std::vector<int>& input) {std::vector<int> result = input;// 模拟耗时处理std::this_thread::sleep_for(std::chrono::milliseconds(500));// 对数据进行一些转换for (auto& value : result) {value = value * 2 + 10;}return result;}// 阶段3: 分析数据static std::pair<double, double> analyzeData(const std::vector<int>& data) {if (data.empty()) {return {0.0, 0.0};}// 模拟耗时分析std::this_thread::sleep_for(std::chrono::milliseconds(300));double sum = 0.0;for (int value : data) {sum += value;}double average = sum / data.size();double variance = 0.0;for (int value : data) {variance += (value - average) * (value - average);}variance /= data.size();return {average, std::sqrt(variance)};}public:void runPipeline() {std::cout << "启动数据处理管道..." << std::endl;// 异步执行第一阶段auto dataFuture = std::async(std::launch::async, generateData, 1000);// 等待数据生成完成,然后启动处理阶段auto processedDataFuture = std::async(std::launch::async, [&]() {return processData(dataFuture.get());});// 同时启动分析阶段auto analysisFuture = std::async(std::launch::async, [&]() {return analyzeData(processedDataFuture.get());});// 等待所有阶段完成并获取结果auto [average, stdDev] = analysisFuture.get();std::cout << "处理完成!" << std::endl;std::cout << "平均值: " << average << std::endl;std::cout << "标准差: " << stdDev << std::endl;}
};int main() {DataProcessingPipeline pipeline;pipeline.runPipeline();return 0;
}

5.2 案例二:并发Web请求聚合

模拟并发发出多个HTTP请求并聚合结果:

#include <future>
#include <vector>
#include <iostream>
#include <string>
#include <chrono>
#include <random>class ConcurrentWebRequests {
private:// 模拟HTTP请求static std::string makeHttpRequest(const std::string& url) {// 模拟网络延迟std::random_device rd;std::mt19937 gen(rd());std::uniform_int_distribution<> delay(100, 2000); // 100ms-2s延迟std::this_thread::sleep_for(std::chrono::milliseconds(delay(gen)));// 模拟响应内容return "Response from " + url + ": {data: " + std::to_string(delay(gen)) + "}";}public:std::vector<std::string> fetchMultipleUrls(const std::vector<std::string>& urls) {std::vector<std::future<std::string>> futures;// 启动所有异步请求for (const auto& url : urls) {futures.push_back(std::async(std::launch::async, makeHttpRequest, url));}std::vector<std::string> results;results.reserve(urls.size());// 等待所有请求完成for (auto& future : futures) {try {results.push_back(future.get());} catch (const std::exception& e) {results.push_back("Error: " + std::string(e.what()));}}return results;}
};int main() {ConcurrentWebRequests fetcher;std::vector<std::string> urls = {"http://api.example.com/data1","http://api.example.com/data2", "http://api.example.com/data3","http://api.example.com/data4"};std::cout << "开始并发请求..." << std::endl;auto start = std::chrono::high_resolution_clock::now();auto responses = fetcher.fetchMultipleUrls(urls);auto end = std::chrono::high_resolution_clock::now();auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);std::cout << "所有请求完成! 耗时: " << duration.count() << "ms" << std::endl;for (const auto& response : responses) {std::cout << response << std::endl;}return 0;
}

5.3 案例三:实时数据流处理系统

构建一个简单的实时数据流处理系统:

#include <future>
#include <queue>
#include <iostream>
#include <random>
#include <chrono>
#include <mutex>
#include <condition_variable>template<typename T>
class ConcurrentQueue {
private:std::queue<T> queue;std::mutex mtx;std::condition_variable cv;public:void push(T value) {std::lock_guard<std::mutex> lock(mtx);queue.push(std::move(value));cv.notify_one();}T pop() {std::unique_lock<std::mutex> lock(mtx);cv.wait(lock, [this]() { return !queue.empty(); });T value = std::move(queue.front());queue.pop();return value;}bool empty() const {std::lock_guard<std::mutex> lock(mtx);return queue.empty();}
};class RealTimeDataProcessor {
private:ConcurrentQueue<double> dataQueue;std::promise<void> stopSignal;std::future<void> processorFuture;// 数据生成器void dataGenerator() {std::random_device rd;std::mt19937 gen(rd());std::normal_distribution<> dist(0.0, 1.0);while (true) {dataQueue.push(dist(gen));std::this_thread::sleep_for(std::chrono::milliseconds(100));}}// 数据处理器void dataProcessor(std::future<void> stopFuture) {double sum = 0.0;int count = 0;try {while (stopFuture.wait_for(std::chrono::milliseconds(0)) != std::future_status::ready) {if (!dataQueue.empty()) {double value = dataQueue.pop();sum += value;count++;if (count % 10 == 0) {std::cout << "处理了 " << count << " 个数据点,平均值: " << (sum / count) << std::endl;}}std::this_thread::sleep_for(std::chrono::milliseconds(50));}} catch (const std::exception& e) {std::cerr << "处理器异常: " << e.what() << std::endl;}std::cout << "处理器正常停止" << std::endl;}public:void start() {// 启动数据生成器std::thread generator(&RealTimeDataProcessor::dataGenerator, this);generator.detach();// 启动处理器processorFuture = std::async(std::launch::async, &RealTimeDataProcessor::dataProcessor, this, stopSignal.get_future());std::cout << "实时数据处理系统已启动" << std::endl;}void stop() {std::cout << "停止系统..." << std::endl;stopSignal.set_value();processorFuture.get();std::cout << "系统已停止" << std::endl;}
};int main() {RealTimeDataProcessor processor;processor.start();// 运行5秒后停止std::this_thread::sleep_for(std::chrono::seconds(5));processor.stop();return 0;
}

第六章:性能优化与最佳实践

6.1 避免常见的性能陷阱

6.1.1 不要过度创建线程
// 错误示范: 为每个小任务创建线程
void processData(const std::vector<int>& data) {std::vector<std::future<void>> futures;for (int value : data) {// 为每个元素创建线程 - 非常低效!futures.push_back(std::async(std::launch::async, [value]() {processSingleValue(value); // 很小的任务}));}for (auto& future : futures) {future.wait();}
}// 正确示范: 使用批量处理
void processDataOptimized(const std::vector<int>& data) {const size_t threadCount = std::thread::hardware_concurrency();const size_t batchSize = data.size() / threadCount;std::vector<std::future<void>> futures;for (size_t i = 0; i < threadCount; ++i) {size_t start = i * batchSize;size_t end = (i == threadCount - 1) ? data.size() : start + batchSize;futures.push_back(std::async(std::launch::async, [&data, start, end]() {for (size_t j = start; j < end; ++j) {processSingleValue(data[j]);}}));}for (auto& future : futures) {future.wait();}
}
6.1.2 合理使用启动策略
void launchPolicyGuidelines() {// 适合异步执行的情况:// 1. CPU密集型任务// 2. 需要并行性的任务auto cpuIntensive = std::async(std::launch::async, []() {return performComplexCalculation();});// 适合延迟执行的情况:// 1. 可能不会被执行的任务// 2. 简单的转换操作auto maybeNeeded = std::async(std::launch::deferred, []() {return simpleTransform();});// 只在确实需要时才执行if (needResult) {auto result = maybeNeeded.get();}
}

6.2 内存管理最佳实践

6.2.1 避免在异步任务中捕获大型对象 by reference
// 危险: 可能悬挂引用
void dangerousExample() {std::vector<int> largeData = getLargeData();auto future = std::async([&largeData]() { // 捕获引用!return processData(largeData);});// largeData可能在使用前就被销毁了!
} // largeData在这里被销毁,但异步任务可能还在运行// 安全: 通过值捕获或共享指针
void safeExample() {std::vector<int> largeData = getLargeData();// 方式1: 通过值捕获(复制数据)auto future1 = std::async([data = largeData]() mutable { // C++14初始化捕获return processData(data);});// 方式2: 使用shared_ptrauto sharedData = std::make_shared<std::vector<int>>(std::move(largeData));auto future2 = std::async([sharedData]() {return processData(*sharedData);});
}

6.3 错误处理与恢复策略

6.3.1 实现重试机制
template<typename Func, typename... Args>
auto retryAsync(int maxRetries, Func&& func, Args&&... args) {return std::async([=]() mutable {int attempts = 0;std::exception_ptr lastException;while (attempts < maxRetries) {try {return func(args...);} catch (...) {lastException = std::current_exception();attempts++;std::this_thread::sleep_for(std::chrono::seconds(1 << attempts)); // 指数退避}}std::rethrow_exception(lastException);});
}void useRetryMechanism() {auto future = retryAsync(3, []() {return unreliableNetworkRequest();});try {auto result = future.get();std::cout << "请求成功: " << result << std::endl;} catch (const std::exception& e) {std::cerr << "所有重试尝试都失败了: " << e.what() << std::endl;}
}

第七章:C++17/20/23中的Future进化

7.1 C++17:Std::Future的增强

C++17为std::future带来了重要改进:

void cpp17Features() {// 1.  then() 方法 Continuation (虽然最终没进入C++17标准,但概念重要)// 概念: 允许链式异步操作auto future = std::async([]() { return 42; }).then([](std::future<int> prev) { return prev.get() * 2; }).then([](std::future<int> prev) {return std::to_string(prev.get());});// 2.  when_all() 和 when_any()auto fut1 = std::async([]() { return 1; });auto fut2 = std::async([]() { return 2; });auto fut3 = std::async([]() { return 3; });// 等待所有future完成auto allFutures = std::when_all(fut1, fut2, fut3);allFutures.then([](auto futuresTuple) {auto& [f1, f2, f3] = futuresTuple;std::cout << "所有结果: " << f1.get() << ", " << f2.get() << ", " << f3.get() << std::endl;});
}

7.2 C++20:协程与Std::Future的融合

C++20的协程为异步编程带来了革命性变化:

#include <coroutine>// 简单的协程future包装
template<typename T>
struct FutureAwaiter {std::future<T> future;bool await_ready() const { return false; }void await_suspend(std::coroutine_handle<> handle) {std::thread([this, handle]() {future.wait();handle.resume();}).detach();}T await_resume() { return future.get(); }
};// 让std::future支持协程await
template<typename T>
auto operator co_await(std::future<T> future) {return FutureAwaiter<T>{std::move(future)};
}// 使用协程的异步函数
std::future<int> asyncCoroutineExample() {auto result1 = co_await std::async([]() { return 20; });auto result2 = co_await std::async([]() { return 22; });co_return result1 + result2;
}

7.3 C++23:Executors与异步编程的未来

C++23引入了Executors,为异步操作提供了统一的执行上下文:

// 概念性代码,展示Executors的用法
void executorExample() {// 创建执行器auto executor = std::thread_pool_executor(4);// 使用执行器执行任务auto future1 = std::async(executor, []() { return task1(); });auto future2 = std::async(executor, []() { return task2(); });// 等待结果auto results = std::when_all(future1, future2).get();std::cout << "结果: " << std::get<0>(results).get() << ", " << std::get<1>(results).get() << std::endl;
}

第八章:调试与性能分析技巧

8.1 Future调试的常见挑战

调试异步代码比调试同步代码更具挑战性。以下是一些实用技巧:

#ifdef DEBUG_FUTURES
#define FUTURE_TRACE(msg) std::cout << "[Future] " << msg << " (thread: " << std::this_thread::get_id() << ")" << std::endl
#else
#define FUTURE_TRACE(msg)
#endifclass TraceFuture {
private:std::future<void> underlyingFuture;std::string taskName;public:template<typename Func>TraceFuture(Func&& func, std::string name) : taskName(std::move(name)) {FUTURE_TRACE("创建任务: " << taskName);underlyingFuture = std::async([func = std::forward<Func>(func), this]() {FUTURE_TRACE("开始执行: " << taskName);try {func();FUTURE_TRACE("成功完成: " << taskName);} catch (const std::exception& e) {FUTURE_TRACE("任务失败: " << taskName << " - " << e.what());throw;}});}void wait() {FUTURE_TRACE("等待任务: " << taskName);underlyingFuture.wait();FUTURE_TRACE("任务等待完成: " << taskName);}// ... 其他方法 ...
};void debugExample() {TraceFuture future([]() {std::this_thread::sleep_for(std::chrono::seconds(1));}, "测试任务");future.wait();
}

8.2 性能分析工具与技术

使用各种工具来分析std::future的性能:

class ProfiledFuture {
private:using Clock = std::chrono::high_resolution_clock;using TimePoint = std::chrono::time_point<Clock>;TimePoint creationTime;TimePoint startTime;TimePoint completionTime;std::string taskName;public:template<typename Func>auto executeWithProfiling(Func&& func) {creationTime = Clock::now();return std::async([func = std::forward<Func>(func), this]() mutable {startTime = Clock::now();auto result = func();completionTime = Clock::now();logProfilingData();return result;});}void logProfilingData() const {auto creationDelay = std::chrono::duration_cast<std::chrono::microseconds>(startTime - creationTime);auto executionTime = std::chrono::duration_cast<std::chrono::microseconds>(completionTime - startTime);auto totalTime = std::chrono::duration_cast<std::chrono::microseconds>(completionTime - creationTime);std::cout << "任务分析: " << taskName << "\n"<< "  创建到启动延迟: " << creationDelay.count() << "μs\n"<< "  执行时间: " << executionTime.count() << "μs\n"<< "  总时间: " << totalTime.count() << "μs\n";}
};

第九章:未来发展趋势与替代方案

9.1 Std::Future的局限性

尽管std::future很强大,但它也有一些局限性:

  1. 缺乏组合性:难以将多个future组合成复杂的工作流
  2. 回调地狱:复杂的异步操作可能导致嵌套的回调
  3. 异常处理复杂:虽然支持异常传递,但复杂场景下仍显不足
  4. 性能开销:对于非常细粒度的任务,开销可能过大

9.2 现代替代方案

9.2.1 Facebook的Folly库
// Folly Futures 提供更丰富的功能
void follyExample() {// 链式操作folly::Future<int> future = folly::makeFuture(42).then([](int value) { return value * 2; }).then([](int value) { return std::to_string(value); });// 复杂的组合auto future1 = folly::makeFuture(1);auto future2 = folly::makeFuture(2);folly::collectAll(future1, future2).then([](std::tuple<int, int> results) {auto [a, b] = results;return a + b;});
}
9.2.2 Boost.Asio
// 使用Boost.Asio进行异步IO
void asioExample() {boost::asio::io_context io;// 异步定时器boost::asio::steady_timer timer(io, std::chrono::seconds(1));timer.async_wait([](const boost::system::error_code& ec) {if (!ec) {std::cout << "定时器触发!" << std::endl;}});// 运行IO上下文io.run();
}

9.3 C++26及以后的展望

未来的C++标准可能会引入更多异步编程特性:

  1. 更好的协程支持:更简洁的异步代码编写方式
  2. 标准Executors:统一的执行上下文管理
  3. 异步算法:标准库中的并行算法
  4. 网络库:标准化的异步网络编程

结语:掌握Std::Future,驾驭并发宇宙

通过这场深入的std::future探险,我们已经从基本概念一路探索到高级应用和未来发展趋势。std::future不仅仅是C++并发编程的一个工具,它代表了一种思维方式——从同步的"命令与控制"模式转向异步的"事件与响应"模式。

记住这些关键要点:

  1. 选择合适的工具std::future适合粗粒度的异步任务,细粒度任务考虑其他方案
  2. 遵循最佳实践:合理管理资源,避免常见陷阱
  3. 拥抱现代特性:C++17/20/23为异步编程带来了强大新工具
  4. 保持代码清晰:异步代码容易变得复杂,保持清晰的结构至关重要

std::future就像并发宇宙中的瑞士军刀——虽然不是万能的,但在正确的场景下使用,它能让你轻松解决复杂的异步编程挑战。现在,带着这些知识,去构建那些高效、响应迅速、优雅的并发应用吧!

未来的并发宇宙等待你的探索,愿std::future成为你可靠的伙伴! 🚀✨


文章转载自:

http://iaQ2K82c.jcbmm.cn
http://tqgoa5Lr.jcbmm.cn
http://fO9D7Gcl.jcbmm.cn
http://9zQL6Uqm.jcbmm.cn
http://UD1FtUcn.jcbmm.cn
http://OJpqPTyK.jcbmm.cn
http://u3PKBLOo.jcbmm.cn
http://FDbriQeB.jcbmm.cn
http://KL7YXT6D.jcbmm.cn
http://BLTAVDEe.jcbmm.cn
http://Dg1VA7Pv.jcbmm.cn
http://D5WTujFH.jcbmm.cn
http://1Ajy4SkE.jcbmm.cn
http://ibRsezie.jcbmm.cn
http://7rq6T1iu.jcbmm.cn
http://4dk6VqjB.jcbmm.cn
http://KYAj0mNq.jcbmm.cn
http://4nmdfaIR.jcbmm.cn
http://LiCRGQtS.jcbmm.cn
http://QCPbbnTu.jcbmm.cn
http://1GB3TgNn.jcbmm.cn
http://EZ8bJNzi.jcbmm.cn
http://rj6LWqvt.jcbmm.cn
http://uqIsAuUo.jcbmm.cn
http://H54wXxYX.jcbmm.cn
http://4EnJYdR6.jcbmm.cn
http://xJBfxlj7.jcbmm.cn
http://VHGbMo8o.jcbmm.cn
http://BnuhpmtT.jcbmm.cn
http://VSi5nwwJ.jcbmm.cn
http://www.dtcms.com/a/387150.html

相关文章:

  • 《LINUX系统编程》笔记p13
  • Spring Cloud-面试知识点(组件、注册中心)
  • 2.2 定点数的运算 (答案见原书 P93)
  • 使用数据断点调试唤醒任务时__state的变化
  • 力扣周赛困难-3681. 子序列最大 XOR 值 (线性基)
  • Spring IOC 与 Spring AOP
  • 【FreeRTOS】队列API全家桶
  • 【Docker项目实战】使用Docker部署Cup容器镜像更新工具
  • (笔记)内存文件映射mmap
  • springboot传输文件,下载文件
  • 基于51单片机的出租车计价器霍尔测速设计
  • 【笔记】Agent应用开发与落地全景
  • C++ STL底层原理系列学习路线规划
  • LAN口和WAN口
  • Dify + Bright Data MCP:从实时影音数据到可落地的智能体生产线
  • 数据库--使用DQL命令查询数据(二)
  • 【FreeRTOS】创建一个任务的详细流程
  • CKA06--storageclass
  • 宝塔安装以及无法打开时的CA证书配置全攻略
  • wend看源码-Open_Deep_Research(LangChain)
  • 摄像头文档识别与透视变化技术和背景建模技术(追踪)
  • 123、【OS】【Nuttx】【周边】效果呈现方案解析:find 格式化打印
  • DC-4靶机渗透
  • 大模型在线对话平台集锦(持续更新ing...)
  • JavaScript中 i++ 与 ++i
  • 【cookie】JavaScript操作增删改查
  • OC-AFNetworking
  • Java全栈学习笔记35
  • kylin v10 系统 上 qt 5.15.17版本构建及使用
  • Linux:基于环形队列的生产者消费模型