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

C++20 中的同步输出流:`std::basic_osyncstream` 深入解析与应用实践

文章目录

    • 一、`std::basic_osyncstream` 的背景与动机
    • 二、`std::basic_osyncstream` 的基本原理
    • 三、`std::basic_osyncstream` 的使用方法
      • (一)基本用法
      • (二)多线程环境下的使用
      • (三)与文件流的结合
    • 四、`std::basic_osyncstream` 的高级特性
      • (一)缓冲区管理
      • (二)与其他 C++20 特性的结合
        • 1\. 与 `std::format` 的结合
        • 2\. 与协程的结合
    • 五、`std::basic_osyncstream` 的性能分析
      • (一)同步机制的开销
      • (二)缓冲区管理的开销
      • (三)性能优化建议
    • 六、`std::basic_osyncstream` 的应用场景
      • (一)日志系统
      • (二)多线程数据处理
      • (三)文件写入
    • 七、`std::basic_osyncstream` 的实现原理
      • (一)`std::basic_syncbuf` 的角色
      • (二)线程安全的实现机制
      • (三)缓冲区刷新策略
    • 八、`std::basic_osyncstream` 的优势与局限性
      • (一)优势
      • (二)局限性
    • 九、`std::basic_osyncstream` 的最佳实践
      • (一)合理设置缓冲区大小
      • (二)减少不必要的同步操作
      • (三)使用线程池
      • (四)避免过度使用 `std::basic_osyncstream`
    • 十、`std::basic_osyncstream` 的未来展望
      • (一)性能优化
      • (二)功能扩展
      • (三)与其他特性的集成
    • 十一、总结

在多线程编程中,输出流的同步问题一直是困扰开发者的一大难题。传统的 std::ostream(如 std::cout)在多线程环境下无法保证输出的顺序性和完整性,容易导致输出内容交织、顺序混乱等问题。为了解决这一问题,C++20 引入了 std::basic_osyncstream,它为多线程环境下的输出流同步提供了一种高效、简洁的解决方案。

一、std::basic_osyncstream 的背景与动机

在多线程程序中,多个线程可能同时尝试向同一个输出流(如控制台或文件)写入数据。由于 std::ostream 本身并不提供线程安全机制,这种并发写入会导致数据竞争(race condition),使得输出结果不可预测。例如,以下代码展示了在多线程环境下使用 std::cout 输出时可能出现的问题:

#include <iostream>
#include <thread>
#include <vector>void print_thread_id(int id) {std::cout << "Thread " << id << " is running\n";
}int main() {constexpr int num_threads = 5;std::vector<std::thread> threads;for (int i = 0; i < num_threads; ++i) {threads.emplace_back(print_thread_id, i);}for (auto& t : threads) {t.join();}return 0;
}

在上述代码中,多个线程同时向 std::cout 输出,可能会导致输出内容交错,例如:

Thread 0 is runningThread 1 is running
Thread 2 is running
Thread 3 is running
Thread 4 is running

为了解决这种问题,C++20 引入了 std::basic_osyncstream。它通过为每个线程提供独立的缓冲区,并在适当的时候将缓冲区的内容原子式地写入目标流,从而保证了输出的顺序性和完整性。

二、std::basic_osyncstream 的基本原理

std::basic_osyncstreamstd::basic_syncbuf 的便捷包装器。其核心思想是利用 RAII(Resource Acquisition Is Initialization)机制,为每个线程创建一个独立的同步缓冲区(sync buffer)。当线程向 std::basic_osyncstream 写入数据时,数据首先被写入到线程的独立缓冲区中,而不是直接写入目标流。只有在以下两种情况下,缓冲区的内容才会被原子式地写入目标流:

  1. 对象析构:当 std::basic_osyncstream 对象析构时,其内部的缓冲区内容会被自动写入目标流。
  2. 显式刷新:调用 std::basic_osyncstreamemit 方法或插入换行符(如 std::endl)时,缓冲区的内容会被刷新到目标流。

这种设计使得 std::basic_osyncstream 能够在不牺牲性能的前提下,提供线程安全的输出流操作。

三、std::basic_osyncstream 的使用方法

(一)基本用法

std::basic_osyncstream 是一个模板类,它依赖于底层流类型(如 std::ostreamstd::wostream)。要使用 std::basic_osyncstream,首先需要包含头文件 <syncstream>,然后创建一个 std::basic_osyncstream 对象,并将其绑定到一个底层流对象上。以下是一个简单的示例:

#include <iostream>
#include <syncstream>int main() {std::osyncstream sync_out(std::cout); // 创建同步输出流对象sync_out << "Hello, ";sync_out << "World!\n";return 0;
}

在上述代码中,std::osyncstream 对象 sync_out 将输出绑定到 std::cout。由于 std::osyncstream 的存在,即使在多线程环境下,输出内容也不会交错。

(二)多线程环境下的使用

std::basic_osyncstream 的主要优势在于它能够解决多线程环境下的输出同步问题。以下是一个多线程输出的示例:

#include <iostream>
#include <syncstream>
#include <thread>
#include <vector>void print_thread_info(std::basic_osyncstream<std::ostream>& sync_out, int id) {sync_out << "Thread " << id << " is running\n";
}int main() {std::basic_osyncstream<std::ostream> sync_out(std::cout);std::vector<std::thread> threads;for (int i = 0; i < 5; ++i) {threads.emplace_back(print_thread_info, std::ref(sync_out), i);}for (auto& t : threads) {t.join();}return 0;
}

在上述代码中,多个线程通过 std::basic_osyncstream 对象 sync_outstd::cout 输出信息。由于 std::basic_osyncstream 的同步机制,每个线程的输出都能够按顺序输出,而不会出现内容交错的情况。

(三)与文件流的结合

std::basic_osyncstream 不仅可以与 std::cout 结合使用,还可以与文件流(如 std::ofstream)一起使用。以下是一个将输出写入文件的示例:

#include <fstream>
#include <syncstream>
#include <thread>
#include <vector>void write_to_file(std::basic_osyncstream<std::ofstream>& sync_out, int id) {sync_out << "Thread " << id << " is writing to file\n";
}int main() {std::ofstream file("output.txt");std::basic_osyncstream<std::ofstream> sync_out(file);std::vector<std::thread> threads;for (int i = 0; i < 5; ++i) {threads.emplace_back(write_to_file, std::ref(sync_out), i);}for (auto& t : threads) {t.join();}return 0;
}

在上述代码中,多个线程通过 std::basic_osyncstream 对象 sync_out 将数据写入文件 output.txt。由于 std::basic_osyncstream 的同步机制,文件中的输出内容是按顺序排列的。

四、std::basic_osyncstream 的高级特性

(一)缓冲区管理

std::basic_osyncstream 的底层依赖于 std::basic_syncbuf,它负责管理缓冲区。std::basic_syncbuf 提供了灵活的缓冲区管理机制,允许开发者自定义缓冲区的大小和行为。例如,可以通过以下方式设置缓冲区的大小:

#include <syncstream>
#include <iostream>int main() {std::osyncstream sync_out(std::cout);sync_out.rdbuf()->pubsetbuf(nullptr, 0); // 禁用缓冲区sync_out << "Hello, World!\n";return 0;
}

在上述代码中,通过调用 pubsetbuf 方法,可以禁用缓冲区或设置缓冲区的大小。

(二)与其他 C++20 特性的结合

C++20 引入了许多新特性,如 std::format 和协程(Coroutines)。std::basic_osyncstream 可以与这些特性结合使用,进一步提升代码的可读性和性能。

1. 与 std::format 的结合

std::format 提供了一种安全、灵活的字符串格式化机制。将 std::basic_osyncstreamstd::format 结合使用,可以简化多线程环境下的日志输出。以下是一个示例:

#include <iostream>
#include <format>
#include <syncstream>
#include <thread>void log_message(std::basic_osyncstream<std::ostream>& sync_out, int thread_id, int value) {sync_out << std::format("Thread [{}] reports value: {}\n", thread_id, value);
}int main() {std::basic_osyncstream<std::ostream> sync_out(std::cout);std::thread t1(log_message, std::ref(sync_out), 1, 42);std::thread t2(log_message, std::ref(sync_out), 2, 100);t1.join();t2.join();return 0;
}

在上述代码中,std::format 负责格式化字符串,而 std::basic_osyncstream 负责同步输出。这种组合使得日志输出既安全又高效。

2. 与协程的结合

协程是 C++20 中引入的一种新的并发编程机制。std::basic_osyncstream 可以与协程结合使用,实现更复杂的并发输出逻辑。以下是一个简单的示例:

#include <iostream>
#include <syncstream>
#include <coroutine>
#include <thread>struct AsyncLog {std::basic_osyncstream<std::ostream>& sync_out;int thread_id;struct promise_type {AsyncLog get_return_object() { return {}; }std::suspend_never initial_suspend() { return {}; }std::suspend_never final_suspend() noexcept { return {}; }void return_void() {}void unhandled_exception() {}};void await_resume() {}void await_suspend(std::coroutine_handle<> h) {sync_out << "Thread " << thread_id << " is logging\n";h.resume();}
};void log_thread(std::basic_osyncstream<std::ostream>& sync_out, int id) {AsyncLog{sync_out, id}.await_suspend(std::noop_coroutine());
}int main() {std::basic_osyncstream<std::ostream> sync_out(std::cout);std::thread t1(log_thread, std::ref(sync_out), 1);std::thread t2(log_thread, std::ref(sync_out), 2);t1.join();t2.join();return 0;
}

在上述代码中,AsyncLog 是一个协程,它通过 std::basic_osyncstream 同步输出日志信息。这种结合使得协程能够与同步输出流无缝协作。

五、std::basic_osyncstream 的性能分析

虽然 std::basic_osyncstream 提供了线程安全的输出流操作,但它可能会引入一定的性能开销。主要的性能开销来自于同步机制和缓冲区管理。以下是一些性能分析的关键点:

(一)同步机制的开销

std::basic_osyncstream 的同步机制基于互斥锁(mutex)。每次线程向 std::basic_osyncstream 写入数据时,都会尝试获取互斥锁。如果多个线程同时尝试写入,可能会导致线程阻塞,从而影响性能。然而,这种开销通常是可以接受的,因为它能够保证输出的顺序性和完整性。

(二)缓冲区管理的开销

std::basic_osyncstream 使用缓冲区来减少对底层流的写入操作。虽然缓冲区可以提高性能,但缓冲区的大小和刷新策略也会影响性能。如果缓冲区过大,可能会导致内存占用增加;如果缓冲区过小,可能会导致频繁的刷新操作。因此,合理设置缓冲区大小是优化性能的关键。

(三)性能优化建议

为了优化 std::basic_osyncstream 的性能,可以采取以下措施:

  1. 合理设置缓冲区大小:根据实际需求调整缓冲区大小,避免缓冲区过大或过小。
  2. 减少不必要的同步操作:如果输出内容较短,可以考虑使用 std::endlstd::flush 显式刷新缓冲区,而不是依赖析构函数自动刷新。
  3. 使用线程池:在多线程环境下,使用线程池可以减少线程创建和销毁的开销,从而提高性能。

六、std::basic_osyncstream 的应用场景

std::basic_osyncstream 在多线程编程中具有广泛的应用场景,以下是一些典型的例子:

(一)日志系统

在多线程应用程序中,日志系统是必不可少的。std::basic_osyncstream 可以用于实现线程安全的日志输出,确保日志信息的顺序性和完整性。以下是一个简单的日志系统实现:

#include <iostream>
#include <syncstream>
#include <thread>
#include <vector>class Logger {
public:static void log(const std::string& message) {std::basic_osyncstream<std::ostream> sync_out(std::cout);sync_out << message << std::endl;}
};void worker_thread(int id) {Logger::log("Thread " + std::to_string(id) + " is running");
}int main() {std::vector<std::thread> threads;for (int i = 0; i < 5; ++i) {threads.emplace_back(worker_thread, i);}for (auto& t : threads) {t.join();}return 0;
}

在上述代码中,Logger 类使用 std::basic_osyncstream 实现线程安全的日志输出。多个线程通过 Logger::log 方法输出日志信息,而不会出现内容交错的情况。

(二)多线程数据处理

在多线程数据处理中,std::basic_osyncstream 可以用于输出处理结果。以下是一个简单的数据处理示例:

#include <iostream>
#include <syncstream>
#include <thread>
#include <vector>void process_data(std::basic_osyncstream<std::ostream>& sync_out, int data) {// 模拟数据处理int result = data * 2;sync_out << "Thread " << std::this_thread::get_id() << " processed data: " << data << ", result: " << result << std::endl;
}int main() {std::basic_osyncstream<std::ostream> sync_out(std::cout);std::vector<std::thread> threads;for (int i = 0; i < 5; ++i) {threads.emplace_back(process_data, std::ref(sync_out), i);}for (auto& t : threads) {t.join();}return 0;
}

在上述代码中,多个线程通过 std::basic_osyncstream 输出数据处理结果。由于 std::basic_osyncstream 的同步机制,输出内容是按顺序排列的。

(三)文件写入

在多线程环境下,向文件写入数据时也需要保证线程安全。std::basic_osyncstream 可以与文件流结合使用,实现线程安全的文件写入。以下是一个示例:

#include <fstream>
#include <syncstream>
#include <thread>
#include <vector>void write_to_file(std::basic_osyncstream<std::ofstream>& sync_out, int data) {sync_out << "Thread " << std::this_thread::this_thread::get_id() << " wrote data: " << data << std::endl;
}int main() {std::ofstream file("output.txt");std::basic_osyncstream<std::ofstream> sync_out(file);std::vector<std::thread> threads;for (int i = 0; i < 10; ++i) {threads.emplace_back(write_to_file, std::ref(sync_out), i);}for (auto& t : threads) {t.join();}return 0;
}

在上述代码中,多个线程通过 std::basic_osyncstream 向文件 output.txt 写入数据。由于 std::basic_osyncstream 的同步机制,文件中的输出内容是按顺序排列的,不会出现数据交错的情况。

七、std::basic_osyncstream 的实现原理

为了更好地理解 std::basic_osyncstream 的工作原理,我们需要深入探讨其底层实现机制。std::basic_osyncstream 是基于 std::basic_syncbuf 的一个封装,而 std::basic_syncbuf 是 C++20 中引入的一个同步缓冲区类模板。

(一)std::basic_syncbuf 的角色

std::basic_syncbufstd::basic_osyncstream 的底层缓冲区管理器。它继承自 std::basic_streambuf,并重写了关键的虚函数,如 overflowsync,以实现同步写入。std::basic_syncbuf 的主要职责是:

  1. 缓冲区管理:为每个线程分配独立的缓冲区,减少对底层流的直接写入操作,从而提高性能。
  2. 同步写入:在缓冲区满或显式刷新时,将缓冲区的内容原子式地写入底层流,确保线程安全。

(二)线程安全的实现机制

std::basic_syncbuf 使用互斥锁(mutex)来实现线程安全的写入操作。当一个线程尝试写入数据时,它会首先获取互斥锁,然后将数据写入缓冲区。如果缓冲区满了或者调用了 emit 方法,缓冲区的内容会被刷新到底层流。在刷新过程中,互斥锁会确保只有一个线程能够访问底层流,从而避免数据竞争。

(三)缓冲区刷新策略

std::basic_syncbuf 的缓冲区刷新策略是影响性能的关键因素之一。缓冲区的刷新可以通过以下几种方式触发:

  1. 显式刷新:调用 std::basic_osyncstreamemit 方法或插入换行符(如 std::endl)时,缓冲区的内容会被刷新到底层流。
  2. 缓冲区满:当缓冲区达到其最大容量时,缓冲区的内容会被自动刷新到底层流。
  3. 对象析构:当 std::basic_osyncstream 对象析构时,其内部的缓冲区内容会被自动写入底层流。

八、std::basic_osyncstream 的优势与局限性

(一)优势

  1. 线程安全std::basic_osyncstream 提供了线程安全的输出流操作,解决了多线程环境下的输出混乱问题。
  2. 性能优化:通过缓冲区管理,减少了对底层流的直接写入操作,从而提高了性能。
  3. 易用性std::basic_osyncstream 的使用方法与传统的 std::ostream 类似,易于上手。
  4. 灵活性:可以与多种底层流(如 std::coutstd::ofstream)结合使用,满足不同的输出需求。

(二)局限性

  1. 性能开销:虽然 std::basic_osyncstream 通过缓冲区管理减少了对底层流的写入操作,但同步机制本身仍会引入一定的性能开销。特别是在高并发场景下,互斥锁的争用可能会导致线程阻塞,从而影响性能。
  2. 缓冲区管理的复杂性:合理设置缓冲区大小是优化性能的关键,但缓冲区大小的设置需要根据具体应用场景进行调整。如果缓冲区过大,可能会导致内存占用增加;如果缓冲区过小,可能会导致频繁的刷新操作。
  3. 对底层流的依赖std::basic_osyncstream 的性能和行为在很大程度上依赖于底层流的实现。例如,如果底层流的写入操作本身就很慢,std::basic_osyncstream 的性能也会受到影响。

九、std::basic_osyncstream 的最佳实践

为了充分发挥 std::basic_osyncstream 的优势,同时避免其局限性带来的影响,以下是一些最佳实践建议:

(一)合理设置缓冲区大小

缓冲区大小的设置需要根据具体应用场景进行调整。一般来说,缓冲区大小应该根据以下因素进行权衡:

  1. 内存占用:较大的缓冲区会占用更多的内存,但可以减少对底层流的写入操作,从而提高性能。
  2. 刷新频率:较小的缓冲区会导致更频繁的刷新操作,从而增加性能开销。
  3. 输出延迟:较大的缓冲区可能会导致输出延迟增加,因为数据需要在缓冲区中积累到一定程度才会被刷新。

在实际应用中,可以通过实验和性能测试来确定最优的缓冲区大小。例如,可以通过以下代码设置缓冲区大小:

#include <iostream>
#include <syncstream>int main() {std::osyncstream sync_out(std::cout);sync_out.rdbuf()->pubsetbuf(nullptr, 1024); // 设置缓冲区大小为 1024 字节sync_out << "Hello, World!\n";return 0;
}

(二)减少不必要的同步操作

虽然 std::basic_osyncstream 提供了线程安全的输出流操作,但过多的同步操作可能会引入不必要的性能开销。为了减少同步操作,可以采取以下措施:

  1. 显式刷新缓冲区:如果输出内容较短,可以考虑使用 std::endlstd::flush 显式刷新缓冲区,而不是依赖析构函数自动刷新。显式刷新可以减少缓冲区的占用时间,从而降低同步操作的开销。
  2. 批量写入:尽量将多个输出操作合并为一个批量操作,减少对 std::basic_osyncstream 的调用次数。例如,可以通过以下代码实现批量写入:
#include <iostream>
#include <syncstream>
#include <sstream>int main() {std::osyncstream sync_out(std::cout);std::ostringstream oss;oss << "Hello, " << "World!\n";sync_out << oss.str();return 0;
}

在上述代码中,通过 std::ostringstream 将多个输出操作合并为一个字符串,然后一次性写入 std::basic_osyncstream,从而减少了同步操作的次数。

(三)使用线程池

在多线程环境下,线程的创建和销毁是一个相对耗时的操作。使用线程池可以减少线程的创建和销毁次数,从而提高性能。线程池预先创建了一组线程,并在需要时将任务分配给这些线程。这样可以避免频繁创建和销毁线程带来的开销。

以下是一个简单的线程池实现示例:

#include <iostream>
#include <syncstream>
#include <thread>
#include <vector>
#include <queue>
#include <functional>
#include <mutex>
#include <condition_variable>class ThreadPool {
public:ThreadPool(size_t num_threads) {for (size_t i = 0; i < num_threads; ++i) {threads.emplace_back([this] {while (true) {std::function<void()> task;{std::unique_lock<std::mutex> lock(queue_mutex);condition.wait(lock, [this] { return stop || !tasks.empty(); });if (stop && tasks.empty()) {return;}task = std::move(tasks.front());tasks.pop();}task();}});}}~ThreadPool() {{std::unique_lock<std::mutex> lock(queue_mutex);stop = true;}condition.notify_all();for (auto& t : threads) {t.join();}}template <typename F, typename... Args>auto enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> {using return_type = typename std::result_of<F(Args...)>::type;auto task = std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));std::future<return_type> res = task->get_future();{std::unique_lock<std::mutex> lock(queue_mutex);if (stop) {throw std::runtime_error("enqueue on stopped ThreadPool");}tasks.emplace([task]() { (*task)(); });}condition.notify_one();return res;}private:std::vector<std::thread> threads;std::queue<std::function<void()>> tasks;std::mutex queue_mutex;std::condition_variable condition;bool stop = false;
};void print_message(std::basic_osyncstream<std::ostream>& sync_out, const std::string& message) {sync_out << message << std::endl;
}int main() {std::basic_osyncstream<std::ostream> sync_out(std::cout);ThreadPool pool(4);pool.enqueue(print_message, std::ref(sync_out), "Message from thread 1");pool.enqueue(print_message, std::ref(sync_out), "Message from thread 2");pool.enqueue(print_message, std::ref(sync_out), "Message from thread 3");pool.enqueue(print_message, std::ref(sync_out), "Message from thread 4");return 0;
}

在上述代码中,ThreadPool 类管理了一个线程池,enqueue 方法用于将任务提交到线程池中。通过使用线程池,可以减少线程的创建和销毁次数,从而提高性能。

(四)避免过度使用 std::basic_osyncstream

虽然 std::basic_osyncstream 提供了线程安全的输出流操作,但在某些情况下,过度使用可能会导致不必要的性能开销。例如,如果输出操作本身不需要线程安全,或者可以通过其他方式实现线程安全,那么可以考虑不使用 std::basic_osyncstream

以下是一些可以避免使用 std::basic_osyncstream 的情况:

  1. 单线程环境:如果程序运行在单线程环境中,那么可以使用传统的 std::ostream,而无需使用 std::basic_osyncstream
  2. 独立输出流:如果每个线程都有自己的独立输出流,那么可以避免使用 std::basic_osyncstream。例如,可以为每个线程创建一个独立的文件流,从而避免线程间的竞争。
  3. 日志系统:在某些情况下,可以使用专门的日志库(如 spdloglog4cpp)来实现线程安全的日志输出,而无需使用 std::basic_osyncstream。这些日志库通常提供了更高效的线程安全机制和更丰富的功能。

十、std::basic_osyncstream 的未来展望

std::basic_osyncstream 是 C++20 中引入的一个重要特性,它为多线程环境下的输出流同步提供了一种高效、简洁的解决方案。随着 C++ 标准的不断发展,std::basic_osyncstream 也可能会得到进一步的改进和优化。

以下是一些可能的发展方向:

(一)性能优化

随着硬件技术的不断发展,多核处理器的性能越来越高。为了充分发挥多核处理器的性能,std::basic_osyncstream 可能会引入更多的性能优化措施。例如,可以使用无锁编程技术(lock-free programming)来减少互斥锁的开销,从而提高性能。

(二)功能扩展

std::basic_osyncstream 目前主要支持输出流的同步操作,但未来可能会扩展其功能,支持更多的同步操作类型。例如,可以引入同步输入流(std::basic_isyncstream),从而实现线程安全的输入操作。

(三)与其他特性的集成

C++ 标准中引入了许多新特性,如协程(Coroutines)、模块(Modules)和概念(Concepts)。未来,std::basic_osyncstream 可能会与这些特性进一步集成,从而提供更强大的功能。例如,可以将协程与 std::basic_osyncstream 结合使用,实现更复杂的并发输出逻辑。

十一、总结

std::basic_osyncstream 是 C++20 中引入的一个重要特性,它为多线程环境下的输出流同步提供了一种高效、简洁的解决方案。通过使用 std::basic_osyncstream,可以避免多线程环境下的输出混乱问题,提高程序的可读性和可维护性。

在使用 std::basic_osyncstream 时,需要注意其性能开销和局限性。通过合理设置缓冲区大小、减少不必要的同步操作和使用线程池等措施,可以充分发挥 std::basic_osyncstream 的优势,同时避免其局限性带来的影响。

随着 C++ 标准的不断发展,std::basic_osyncstream 也可能会得到进一步的改进和优化。未来,我们可以期待 std::basic_osyncstream 在性能、功能和与其他特性的集成方面取得更大的进步。

总之,std::basic_osyncstream 是一个多线程编程中不可或缺的工具,它为开发者提供了一种简单而强大的方式来解决多线程环境下的输出流同步问题。通过深入理解其原理和使用方法,我们可以更好地利用这一特性,提升程序的质量和性能。

相关文章:

C++20 中的同步输出流:`std::basic_osyncstream` 深入解析与应用实践

文章目录 一、std::basic_osyncstream 的背景与动机二、std::basic_osyncstream 的基本原理三、std::basic_osyncstream 的使用方法&#xff08;一&#xff09;基本用法&#xff08;二&#xff09;多线程环境下的使用&#xff08;三&#xff09;与文件流的结合 四、std::basic_…...

美摄接入DeepSeek等大模型,用多模态融合重构视频创作新边界!

今年以来&#xff0c;DeepSeek凭借其强大的深度推理分析能力&#xff0c;在AI领域掀起新的热潮。美摄科技快速响应市场需求&#xff0c;迅速接入以DeepSeek、通义千问、商汤、文心一言为代表的大模型&#xff0c;为企业视频创作生产带来全新体验。 传统视频创作面临着同质化、…...

Git 使用笔记

参考链接&#xff1a; 创建版本库 - Git教程 - 廖雪峰的官方网站 Git使用教程,最详细&#xff0c;最傻瓜&#xff0c;最浅显&#xff0c;真正手把手教 - 知乎 命令使用 cd f: 切换目录到 F 盘 cd gitCxl 切换目录到 gitCxl 文件夹 mkdir gitCxl 创建新文件…...

C#:深入理解Thread.Sleep与Task.Delay

1.核心区别概述 特性Thread.SleepTask.Delay阻塞类型同步阻塞当前线程异步非阻塞&#xff0c;释放线程适用场景同步代码中的简单延时异步编程中的非阻塞等待资源消耗占用线程资源&#xff08;线程挂起&#xff09;不占用线程&#xff08;通过计时器回调&#xff09;精度依赖操…...

基于Redis实现共享token登录

文章目录 1.集群下session共享存在的问题2.基于Redis实现共享session存储业务流程图3.具体登录的代码实现3.1 引入redis数据库3.2 发送验证码到前端3.2 登录注册功能实现3.2刷新token有效期(LoginIntereceptor)3.3 MvcConfig配置4.拦截器优化4.1增加RefreshTokenInterceptor 4.…...

【Linux我做主】浅谈Shell及其原理

浅谈Linux中的Shell及其原理 Linux中Shell的运行原理github地址前言一、Linux内核与Shell的关系1.1 操作系统核心1.2 用户与内核的隔离 二、Shell的演进与核心机制2.1 发展历程2.2 核心功能解析2.3 shell的工作流程1. 用户输入命令2. 解析器拆分指令3. 扩展器处理动态内容变量替…...

vulhub/Billu_b0x靶机----练习攻略

1.Billu_b0x靶场下载链接&#xff1a; https://download.vulnhub.com/billu/Billu_b0x.zip 2.下载后&#xff0c;解压出ova文件&#xff0c;直接拖至VMware中&#xff0c;重命名和选择存储位置&#xff0c;点击导入&#xff0c;报错点击重试即可。修改网卡为NAT模式。 打开靶…...

【华为OD-E卷 -121 消消乐游戏 100分(python、java、c++、js、c)】

【华为OD-E卷 - 消消乐游戏 100分(python、java、c++、js、c)】 题目 游戏规则:输入一个只包含英文字母的字符串,字符串中的两个字母如果相邻且相同,就可以消除。 在字符串上反复执行消除的动作,直到无法继续消除为止,此时游戏结束。 输出最终得到的字符串长度 输入描…...

Qt之自定义界面组件 一

通过qt中的painter绘图事件绘制一个电池电量图的变化。效果如下图 创建一个基于界面widget工程&#xff0c;在wdiget界面添加一个widget界面,将添加的widget界面的类提升为Tbattery.在Tbattery类中重写painEvent电池电量代码 文件目录结构 主要部分代码 //Tbattery.cpp #inc…...

破解验证码新利器:基于百度OCR与captcha-killer-modified插件的免费调用教程

破解验证码新利器&#xff1a;基于百度OCR与captcha-killer-modified插件的免费调用教程 引言 免责声明&#xff1a; 本文提供的信息仅供参考&#xff0c;不承担因操作产生的任何损失。读者需自行判断内容适用性&#xff0c;并遵守法律法规。作者不鼓励非法行为&#xff0c;保…...

1-1 MATLAB深度极限学习机

本博客来源于CSDN机器鱼&#xff0c;未同意任何人转载。 更多内容&#xff0c;欢迎点击本专栏目录&#xff0c;查看更多内容。 参考[1]魏洁.深度极限学习机的研究与应用[D].太原理工大学[2023-10-14].DOI:CNKI:CDMD:2.1016.714596. 目录 0.引言 1.ELM-AE实现 2.DE…...

函数的介绍

1.函数的概念 在C语言中也有函数的概念&#xff0c;有些翻译为&#xff1a;子程序&#xff0c;这种翻译更为准确。C语言的函数就是一个完成某项特定的任务的一小段代码。这段代码是有特殊的写法和调用方法的。 C语言的程序其实是有无数个小的函数组合而成的&#xff0c;也可以…...

4.3--入门知识扫盲,IPv4的头部报文解析,数据报分片,地址分类(包你看一遍全部记住)

IPv4协议&#xff1a;网络世界的快递包裹指南&#xff08;附拆箱说明书&#xff09; “IPv4就像一张明信片&#xff0c;既要写清楚地址&#xff0c;又要控制大小别超重” —— 某网络工程师的桌面铭牌 一、IPv4报头&#xff1a;快递面单的终极艺术 1.1 报头结构图&#xff08;…...

Linux生成自签名证书

一、安装OpenSSL 首先确保机器已安装OpenSSL工具OpenSSL的安装可参考&#xff1a;源码编译OpenSSL 二、生成私钥​​​​​​ openssl genpkey -algorithm RSA -out server.key 三、创建证书签署请求(CSR) openssl req -new -key server.key -out server.csr 四、生成自签…...

烽火HG680-KA_海思HI3798MV310_安卓9.0_U盘强刷固件包及注意点说明

之前发布过这个固件包&#xff0c;关于烽火HG680-KA&#xff0f;HG680-KB_海思HI3798MV310_安卓9.0_U盘强刷固件包详细说明一下&#xff0c;汇总总结一些常遇到的情况&#xff0c;这次固件会分开发布&#xff0c;以免混淆。 上一个帖子地址&#xff1a;https://blog.csdn.net/…...

Vue3 核心特性解析:Suspense 与 Teleport 原理深度剖析

Vue3 核心特性解析&#xff1a;Suspense 与 Teleport 原理深度剖析 一、Teleport&#xff1a;突破组件层级的时空传送 1.1 实现原理图解 #mermaid-svg-75dTmiektg1XNS13 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-s…...

Linux Vim 寄存器 | 从基础分类到高级应用

注&#xff1a;本文为 “vim 寄存器” 相关文章合辑。 英文引文&#xff0c;机翻未校。 中文引文&#xff0c;略作重排。 未整理去重&#xff0c;如有内容异常&#xff0c;请看原文。 Registers 寄存器 Learning Vim registers is like learning algebra for the first ti…...

为什么TCP需要三次握手?一次不行吗?

文章目录 1. 三次握手的过程2. 为什么需要三次握手&#xff1f;3. 握手过程中每一步的具体作用4. 简单比喻5. 为什么是三次握手&#xff0c;而不是两次或四次&#xff1f;6. 三次握手中的序列号有什么作用&#xff1f;7. 总结 1. 三次握手的过程 三次握手是建立 TCP 连接的过程…...

OpenGL ES 入门指南:从基础到实战

引言&#xff1a;为什么需要 OpenGL ES&#xff1f; 在当今的嵌入式设备&#xff08;如智能手机、汽车仪表盘、智能家居中控屏&#xff09;中&#xff0c;流畅的图形渲染能力是用户体验的核心。OpenGL ES&#xff08;OpenGL for Embedded Systems&#xff09; 作为行业标准&am…...

台达PLC转太网转换的教程案例(台达DVP系列)

产品介绍 台达DVP-PLC自投身工业自动化市场以来&#xff0c;始终致力于创新发展&#xff0c;不断推陈出新。其产品紧密贴合市场需求与行业工艺&#xff0c;凭借卓越性能与丰富功能&#xff0c;深受用户青睐。不仅推出了高效的程序与编辑工具&#xff0c;显著提升了主机执行速度…...

Ubuntu24.10编译Android12源码并运行于模拟器中

效果如下&#xff1a; 初始化环境&#xff1a; 运行lunch弹出对应目标 生成模拟器版本镜像 镜像生成成功 生成模拟器启动镜像 编译注意事项: 24.10版本&#xff1a; sudo apt install curl curl -sSL https://gerrit-googlesource.proxy.ustclug.org/git-repo//master/r…...

Java面试易忽略知识点

1. CompletableFuture中thenApply()与thenCompose()的区别 考察点&#xff1a;组合式异步编程 解析&#xff1a; ​**thenApply()**&#xff1a;接收前序任务结果&#xff0c;返回普通对象&#xff08;同步转换&#xff09;&#xff0c;适用简单数据处理。​**thenCompose()*…...

C#通过SignalR直接返回流式响应内容

1、背景 实现流式响应基本上分为两大技术方案&#xff1a;&#xff08;1&#xff09;基于HTTP的Stream处理&#xff1b;&#xff08;2&#xff09;基于socket的连接。前者的实现方式有&#xff1a;《C#通过API接口返回流式响应内容—SSE方式》和《C#通过API接口返回流式响应内…...

【排序算法对比】快速排序、归并排序、堆排序

排序算法对比&#xff1a;快速排序、归并排序、堆排序 1. 快速排序&#xff08;Quick Sort&#xff09; 原理 快速排序采用 分治法&#xff08;Divide and Conquer&#xff09;&#xff0c;通过选取基准值&#xff08;pivot&#xff09;&#xff0c;将数组划分为 小于基准值…...

YOLO11改进-模块-引入空间带状注意力机制(Spatial Strip Attention,SSA)增强模型对空间信息处理能力的重要模块

在图像相关任务中&#xff0c;传统卷积神经网络&#xff08;CNN&#xff09;在处理空间信息时&#xff0c;卷积核的感受野有限&#xff0c;难以有效捕捉长距离的空间依赖关系。而自注意力机制虽然能建模长距离依赖&#xff0c;但计算复杂度较高。为了在高效计算的同时更好地捕捉…...

C++内存分配方式

文章目录 1、静态内存分配2、栈内存分配3、堆内存分配4、内存池分配5、placement new语法工作原理示例 placement new应用场景 在C 中&#xff0c;内存分配主要有以下几种方式&#xff1a; 1、静态内存分配 特点&#xff1a;在编译时就确定了内存的分配和释放&#xff0c;内存…...

【经验】Orin系列Ubuntu远程桌面:VNC、NoMachine、URDC

1、VNC 1.1 Ubuntu端 1)安装VNC服务器 sudo apt install tigervnc-standalone-server2)安装xfce4 桌面 xfce4 用资源较GNOME ,KDE较少。适合老机器,轻量级桌面。与windows界面环境类似。 sudo apt install xfce4 xfce4-goodies也可以使用其它的桌面系统,可以使用如下命…...

【RabbitMQ】RabbitMQ消息的重复消费问题如何解决?

可以从消息队列和消费者两方面入手&#xff0c;确保消息处理的幂等性和可靠性。 1.消息重复消费的原因 1.1消息队列的机制 消息确认失败&#xff1a; 消费者处理完消息后&#xff0c;未正确发送确认(ACK)给RabbitMQ&#xff0c;导致消息被重新投递。消息重试机制&#xff1a…...

Python、MATLAB和PPT完成数学建模竞赛中的地图绘制

参加数学建模比赛时&#xff0c;很多题目——诸如统计类、数据挖掘类、环保类、建议类的题目总会涉及到地理相关的情景&#xff0c;往往要求我们制作与地图相关的可视化内容。如下图&#xff0c;这是21年亚太赛的那道塞罕坝的题目&#xff0c;期间涉及到温度、降水和森林覆盖率…...

Git 分支删除操作指南(含本地与远程)

&#x1f680; Git 分支删除操作指南&#xff08;含本地与远程&#xff09; 在多人协作的开发过程中&#xff0c;定期清理已合并的临时分支&#xff08;如 feature/*、bugfix/*、hotfix/* 等&#xff09;可以保持仓库整洁&#xff0c;避免混乱。 &#x1f4cc; 分支命名规范回…...

视频推拉流EasyDSS点播平台云端录像播放异常的问题排查与解决

视频推拉流EasyDSS视频直播点播平台可提供一站式的视频转码、点播、直播、视频推拉流、播放H.265视频等服务&#xff0c;搭配RTMP高清摄像头使用&#xff0c;可将无人机设备的实时流推送到平台上&#xff0c;实现无人机视频推流直播、巡检等应用。 有用户反馈&#xff0c;项目现…...

v-model+computed实现父子组件数据传递和同步

v-modelcomputed实现父子组件数据传递和同步 1. 父组件2. 子组件说明总结 1. 父组件 <template><div><span>父子组件传值&#xff1a;{{countRef}}<my-counter v-modelcount></my-counter></span></div> </template> <scr…...

一键秒连WiFi智能设备,uni-app全栈式物联开发指南。

如何使用 uni-app 框架实现通过 WiFi 连接设备并进行命令交互的硬件开发。为了方便理解和实践&#xff0c;我们将提供相应的源代码示例&#xff0c;帮助开发者快速上手。 1. 硬件准备 在开始之前&#xff0c;请确保你已经准备好以下硬件设备&#xff1a; 支持 WiFi 连接的设备…...

关于Docker是否被淘汰虚拟机实现连接虚拟专用网络Ubuntu 22.04 LTS部署Harbor仓库全流程

1.今天的第一个主题&#xff1a; 第一个主题是关于Docker是否真的被K8S弃用&#xff0c;还是可以继续兼容&#xff0c;因为我们知道在去年的时候&#xff0c;由于不可控的原因&#xff0c;docker的所有国内镜像源都被Ban了&#xff0c;再加上K8S自从V1.20之后&#xff0c;宣布…...

【C++】动态规划从入门到精通

一、动态规划基础概念详解 什么是动态规划 动态规划&#xff08;Dynamic Programming&#xff0c;DP&#xff09;是一种通过将复杂问题分解为重叠子问题&#xff0c;并存储子问题解以避免重复计算的优化算法。它适用于具有以下两个关键性质的问题&#xff1a; 最优子结构&…...

【专栏预告】《VR 360°全景视频开发:从GoPro到Unity VR眼镜应用实战》

【专栏预告】每周天12:00更新&#xff0c;欢迎指导与交流。 专栏地址&#xff1a;《VR 360全景视频开发&#xff1a;从GoPro到Unity VR眼镜应用实战》 前言 随着VR技术的不断发展&#xff0c;360全景视频的需求也在逐年增长。尤其是在VR眼镜端&#xff0c;360全景视频带来了…...

【leetcode100】搜索插入位置

1、题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 示例 1: 输入: nums [1,3,5,6], target 5 输出: 2…...

Java面试黄金宝典3

1. 什么是 NIO 原理 缓冲区&#xff08;Buffer&#xff09;&#xff1a; 它是一个线性的、有限的基本数据元素序列&#xff0c;本质上是一块内存区域&#xff0c;被包装成了一个对象&#xff0c;以便于进行高效的数据读写操作。不同类型的基本数据都有对应的Buffer子类&#xf…...

vue3 报错 Could not find a declaration file for module ‘/App.vue‘

vue3 报错 Could not find a declaration file for module /App.vue.app.vue路径.js implicitly has an any type 问题描述原因分析&#xff1a;解决方案&#xff1a; 问题描述 Could not find a declaration file for module /App.vue.app.vue路径.js implicitly has an any …...

linux对串口设备文件进行重命名(删除、重建)

0.前言 最近在弄3562的自制板&#xff0c;有很多串口&#xff0c;然后发现设备文件名编号有些跳跃&#xff0c;不方便用户使用&#xff0c;因此&#xff0c;需要对这些设备文件进行重命名 1.查看设备号 我们需要知道目标设备文件的设备号&#xff0c;通过ls -l /dev/tty*查看…...

Linux内核传输层UDP源码分析

一、用户数据包协议&#xff08;UDP&#xff09; 1.UDP数据报头 UDP 提供面向消息的不可靠传输&#xff0c;但没有拥塞控制功能。很多协议都使用 UDP&#xff0c;如用于 IP 网络传输音频和视频的实时传输协议 (Real-time Transport Protocol&#xff0c;RTP)&#xff0c;此类型…...

GitHub 超火的开源终端工具——Warp

Warp 作为近年来 GitHub 上备受瞩目的开源终端工具&#xff0c;以其智能化、高性能和协作能力重新定义了命令行操作体验。以下从多个维度深入解析其核心特性、技术架构、用户评价及生态影响力&#xff1a; 一、背景与核心团队 Warp 由前 GitHub CTO Jason Warner 和 Google 前…...

【Java基础巩固系列】异常

业务背景 业务开发中&#xff0c;总会遇到代码出现异常的情况&#xff0c;不合理的异常处理或不处理异常除了影响业务功能和中断业务功能外&#xff0c;还会增加排查问题的难度。所以我们要学会正确的使用异常处理。合理的异常处理能减少很多潜在的问题&#xff0c;是提高代码…...

sass介绍

1、Sass简介 Sass 是一种 CSS 的预编译语言。它提供了 变量&#xff08;variables&#xff09;、嵌套&#xff08;nested rules&#xff09;、 混合&#xff08;mixins&#xff09;、 函数&#xff08;functions&#xff09;等功能&#xff0c;并且完全兼容 CSS 语法。Sass 能…...

第1章:云原生时代:容器技术的发展历程与核心价值

第1章:云原生时代:容器技术的发展历程与核心价值 作者:DogDog_Shuai 阅读时间:约15分钟 难度:入门级 目录 1. 引言2. 容器技术的发展历程3. 容器技术的核心价值4. 云原生时代的机遇与挑战5. 总结1. 引言...

软考程序员考试知识点汇总

软考程序员考试&#xff08;初级资格&#xff09;主要考察计算机基础理论、编程能力及软件开发相关知识。以下是核心知识点总结及备考建议&#xff1a; 一、计算机基础 数制与编码 二进制、八进制、十进制、十六进制转换原码、反码、补码表示&#xff08;整数与浮点数&#xf…...

JVM OOM问题如何排查和解决

在 Java 开发中&#xff0c;JVM OOM&#xff08;OutOfMemoryError&#xff09;问题通常是指程序运行时&#xff0c;JVM 无法为对象分配足够的内存空间&#xff0c;导致发生内存溢出的错误。这个问题往往和内存的配置、内存泄漏、或者资源过度使用等因素有关。 1. OOM 错误类型…...

折叠树报表

折叠树报表中包含了三种信息: 1.树组织信息-可展开、收拢 2.节点的统计信息(汇总求和) 3.每个节点对应的数据信息 一、准备数据 mysql8 数据库中存在两张表 org和store表。 org表和部分数据如下,其中orgname是组织的名称,codepath是完整的组织代码,seq是每个节点的顺序,可…...

python 数据可视化matplotib库安装与使用

要使用 matplotlib 库进行数据可视化&#xff0c;首先你需要确保已经安装了该库。如果你还没有安装&#xff0c;可以通过 Python 的包管理器 pip 来安装它。在你的命令行工具中运行以下命令来安装 matplotlib&#xff1a; pip install matplotlib安装完成后&#xff0c;你就可以…...

Springdoc配置参数详解

文章目录 **1. 基础配置****API 文档路径-springdoc.api-docs.path****Swagger UI 路径-springdoc.swagger-ui.path****是否启用 API 文档-springdoc.api-docs.enabled****是否启用 Swagger UI-springdoc.swagger-ui.enabled** **2. 全局元信息-info****应用标题-springdoc.inf…...