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

突破编程_C++_高级教程(多线程编程实例)

1 生产者-消费者模型

生产者-消费者模型是一种多线程协作的设计模式,它主要用于处理生产数据和消费数据的过程。在这个模型中,存在两类线程:生产者线程和消费者线程。生产者线程负责生产数据,并将其放入一个共享的数据缓冲区(通常是一个队列)。消费者线程则从该数据缓冲区中取出数据进行处理。这种模型的核心在于确保生产者和消费者之间的同步和互斥,以防止数据丢失或重复处理。
生产者-消费者模型的适用场景:
生产者-消费者模型广泛适用于需要处理并发数据流的场景,其中数据的生成和处理速度可能不同。以下是几个具体的应用场景:
线程池
在实现线程池的技术点中,任务队列就是一个典型的生产者-消费者模型的应用。当线程池繁忙时,新提交的任务会被放入任务队列(相当于数据缓冲区)等待处理。一旦有线程空闲出来,它会从队列中取出任务进行处理。
网络编程
在生产者-消费者模型中,生产者可以代表网络数据的接收方,将接收到的数据放入缓冲区;而消费者可以代表数据的处理方,从缓冲区中取出数据进行处理。这种模型可以有效地处理网络延迟和数据流的不稳定性。
数据库操作
在数据库操作中,生产者可以代表数据的写入操作,将数据写入数据库;消费者可以代表数据的读取操作,从数据库中读取数据进行处理。生产者-消费者模型可以有效地处理数据库的读写并发问题。
文件操作
在处理大文件或数据流时,生产者可以代表数据的读取操作,将读取的数据放入缓冲区;消费者可以代表数据的处理操作,从缓冲区中取出数据进行处理。这种模型可以有效地提高文件处理的效率。
在 C++11 中,可以使用 <thread>, <mutex>, <condition_variable> 和 <queue> 等库来实现生产者-消费者模型。如下为样例代码:

#include <iostream>  
#include <thread>  
#include <vector>  
#include <queue>  
#include <mutex>  
#include <condition_variable>  std::queue<int> g_datas;		// 全局生产产品
int g_dataIndex = 0;			// 全局生产产品的编号std::mutex g_mutex;
std::condition_variable g_cv;const int MAX_NUM = 3;			// 一次生产产品的数量void producer(int id)
{for (int i = 0; i < MAX_NUM; ++i){std::unique_lock<std::mutex> lock(g_mutex);g_datas.push(g_dataIndex);printf("producer %d produced item : %d\n", id, g_dataIndex);g_dataIndex++;g_cv.notify_one();lock.unlock();std::this_thread::yield();		// 计算机核数较多,并且性能较好的情况下,该语句可以不用加,这样生产者-消费者的整体效率更高。}// 通知生产结束  g_cv.notify_all();
}void consumer() 
{while (true) {std::unique_lock<std::mutex> lock(g_mutex);g_cv.wait(lock, [] { return !g_datas.empty(); });int data = g_datas.front();g_datas.pop();printf("consumer consumed item : %d\n", data);}
}int main() 
{std::vector<std::thread> producers;std::thread consumerThread(consumer);// 创建生产者线程 for (int i = 0; i < 3; i++){producers.emplace_back(producer, i);}// 等待生产结束for (auto& t : producers){t.join();}consumerThread.join();return 0;
}

上面代码的输出为:

producer 0 produced item : 0
consumer consumed item : 0
producer 0 produced item : 1
producer 0 produced item : 2
consumer consumed item : 1
consumer consumed item : 2
producer 2 produced item : 3
producer 2 produced item : 4
consumer consumed item : 3
consumer consumed item : 4
producer 1 produced item : 5
producer 2 produced item : 6
consumer consumed item : 5
consumer consumed item : 6
producer 1 produced item : 7
consumer consumed item : 7
producer 1 produced item : 8
consumer consumed item : 8

在上面代码中,创建了多个生产者线程和一个消费者线程。每个生产者线程都按照产品序列号进行产品生产,并将其推送到共享队列 g_datas 中。消费者线程则等待队列中有元素可用时,从中取出元素并处理。当所有生产者线程完成生产后,它们通过 g_cv.notify_all(); 通知消费者线程来结束生产。
注意以下几点:
使用 std::mutex 来保护共享队列 g_datas 的访问,确保同一时间只有一个线程可以修改它。
使用 std::condition_variable 来在队列为空时阻塞消费者线程,直到有生产者线程向队列中添加新元素。
std::unique_lock 与 std::lock_guard 类似,但提供了更多的灵活性,如手动解锁和条件等待。
std::this_thread::yield() 用于让出CPU时间片,使其他线程有机会运行。但是在计算机核数较多,并且性能较好的情况下,该语句可以不用加,这样生产者-消费者的整体效率更高。
这个模型可以扩展为多个消费者线程,只需创建更多的消费者线程实例即可。在实际应用中,可能还需要考虑其他因素,如队列的大小限制、线程的同步问题、以及优雅地处理线程的启动和停止等。

2 线程池

线程池是一种在并发编程中常用的技术,它用于管理和重用线程。线程池的基本思想是在应用程序启动时创建一定数量的线程,并将它们保存在一个线程池中。当需要执行任务时,从线程池中获取一个空闲的线程来执行该任务。当任务执行完毕后,线程将返回到线程池,以供其他任务复用。线程池的设计目标是避免频繁地创建和销毁线程所带来的开销,以及控制并发执行的线程数量,从而提高系统的性能和资源利用率。
线程池通常包含以下几个关键组成部分:
线程池管理器
负责创建、管理和控制线程池。它负责线程的创建、销毁和管理,以及线程池的状态监控和调度任务。
工作队列
用于存储待执行的任务。当线程池中的线程都在执行任务时,新的任务会被放入工作队列中等待执行。
线程池线程
实际执行任务的线程。线程池中会维护一组线程,这些线程可以被重复使用,从而避免了频繁创建和销毁线程的开销。
线程池的运行机制如下:当任务到达时,线程池管理器会检查线程池中是否有空闲的线程。如果有,则将任务分配给空闲线程执行;如果没有,则根据线程池的配置来决定是创建一个新线程还是将任务放入工作队列中等待执行。当线程池中的线程执行完任务后,会从工作队列中获取下一个任务并执行。
线程池适用于以下场景:
任务量巨大且单个任务执行时间较短
当有大量任务需要执行,且每个任务的执行时间相对较短时,使用线程池可以显著提高程序的执行效率。线程池可以避免频繁地创建和销毁线程,减少资源消耗。
需要控制并发度
线程池可以限制并发执行的线程数量,防止系统过载。通过调整线程池的大小,可以控制并发度,避免资源消耗过大。
提供线程管理和监控
线程池提供了一些管理和监控机制,例如线程池的创建、销毁、线程状态的监控等,方便开发人员进行线程的管理和调试。
总体而言,线程池是一种高效、灵活的并发编程技术,适用于多种场景。通过合理地配置线程池的大小和任务队列的容量,可以充分利用系统资源,提高程序的性能和响应速度。
在 C++11 中,可以使用 <thread>, <mutex>, <condition_variable> 和 <queue> 等库来实现线程池。如下为样例代码:

#include <iostream>  
#include <vector>  
#include <queue>  
#include <thread>  
#include <mutex>  
#include <condition_variable>  
#include <functional>  
#include <future>  class ThreadPool 
{
public:ThreadPool(size_t);template<class F, class... Args>auto enqueue(F&& f, Args&&... args)->std::future<typename std::result_of<F(Args...)>::type>;~ThreadPool();
private:// 需要保持线程活动的标记  std::vector< std::thread > workers;// 任务队列  std::queue< std::function<void()> > tasks;// 同步  std::mutex queueMutex;std::condition_variable condition;bool stop;
};// 构造函数  
inline ThreadPool::ThreadPool(size_t num): stop(false)
{for (size_t i = 0; i < num; i++){workers.emplace_back([this] {while (true){std::function<void()> task;{std::unique_lock<std::mutex> lock(this->queueMutex);this->condition.wait(lock,[this] { return this->stop || !this->tasks.empty(); });if (this->stop && this->tasks.empty()){return;}task = std::move(this->tasks.front());this->tasks.pop();}task();}});}
}// 添加新工作项到线程池  
template<class F, class... Args>
auto ThreadPool::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(queueMutex);// 不允许在停止后添加任务  if (stop) {throw std::runtime_error("enqueue on stopped ThreadPool");}tasks.emplace([task]() { (*task)(); });}condition.notify_one();return res;
}// 析构函数  
inline ThreadPool::~ThreadPool() 
{{std::unique_lock<std::mutex> lock(queueMutex);stop = true;}condition.notify_all();for (std::thread &worker : workers) {worker.join();}
}// 使用线程池的例子  
void doSomething(int n) 
{printf("doing something with %d\n",n);std::this_thread::sleep_for(std::chrono::seconds(1));
}int main() {ThreadPool pool(4);// 添加任务到线程池  auto fut1 = pool.enqueue(doSomething, 1);auto fut2 = pool.enqueue(doSomething, 2);auto fut3 = pool.enqueue(doSomething, 3);auto fut4 = pool.enqueue(doSomething, 4);// 等待所有任务完成  fut1.get();fut2.get();fut3.get();fut4.get();return 0;
}

上面代码的输出为:

doing something with 1
doing something with 2
doing something with 3
doing something with 4

上面代码中的 enqueue 函数是是线程池的核心,它接受一个可调用对象(函数、 Lambda 表达式等)和一组参数,并将它们封装到一个 std::packaged_task 对象中。 std::packaged_task 是一个模板类,它接受一个可调用对象,并将其包装成一个任务,这个任务可以异步执行,并且可以在将来某个时间点获取其结果。
一旦任务被封装,它就被添加到线程池的任务队列中。然后,通过调用condition.notify_one()来唤醒一个等待在条件变量上的线程(如果有的话)。这个被唤醒的线程会从队列中取出任务并执行它。
enqueue 函数返回一个 std::future 对象,这个对象代表了异步任务的结果。调用者可以通过这个 std::future 对象来获取任务的结果,或者等待任务完成。
在main函数中,首先创建了一个包含 4 个线程的线程池。然后向线程池添加了 4 个任务,每个任务都调用 doSomething 函数并传入一个不同的参数。每个任务都返回一个 std::future 对象,我们可以通过这些对象来等待任务完成并获取结果。
最后,通过调用 get() 方法来等待每个任务完成并获取其结果。因为 doSomething 函数没有返回任何值,所以 get() 方法在这里实际上没有做任何事情。如果 doSomething 函数返回了一个值,那么get()方法会返回这个值。

3 定时器

定时器是一个可以设定在某一特定时间点触发某个操作或事件的系统工具。它可以基于时间周期来执行任务,或者在某些特定时间间隔后执行某个操作。定时器的主要作用是产生一个时基,即从某一时刻开始,经过一段指定的时间,触发一个中断或超时回调事件,可以在中断或者超时回调函数中处理数据。
定时器的适用场景非常广泛,如下是几种常见的用途:
嵌入式系统
在嵌入式系统中,定时器是一个基础服务,如 RTOS (实时操作系统)就需要依赖定时器提供时钟节拍以实现线程延时、线程时间片轮询调度等。
操作系统
在操作系统中,定时器用于实现各种定时任务,如定时清理缓存、定时检查系统资源使用情况等。
网络编程
在网络编程中,定时器常用于实现超时控制,如TCP连接超时、请求超时等。
任务调度
在任务调度系统中,定时器可以用于按照预设的时间间隔执行某些任务,如每日的数据统计、报告生成等。
在 C++11 中,可以使用 <thread>, <mutex>, <condition_variable> 和 <queue> 等库来实现线程池。如下为样例代码:

#include <iostream>  
#include <thread>  
#include <chrono>  
#include <atomic>  
#include <mutex>  
#include <condition_variable>  
#include <functional>  class Timer {
public:Timer() : expired(true), tryToExpire(false) {}void start(uint64_t interval, std::function<void()> task){if (expired == false) {// 上一个定时器还在运行, 设置一个标志让线程尽快结束当前等待并退出  tryToExpire = true;// 等待线程结束  if (thread.joinable()){thread.join();}}expired = false;tryToExpire = false;// 保存任务和间隔时间  this->task = task;this->interval = std::chrono::milliseconds(interval);// 启动定时器线程  thread = std::thread([this]() {while (!expired) {std::unique_lock<std::mutex> lock(this->mtx);// 检查是否需要尽快结束等待  if (tryToExpire){// 通过notify_all唤醒可能在等待的线程,并立即返回  this->cv.notify_all();continue;}// 等待定时器到期或收到退出通知 this->cv.wait_for(lock, this->interval, [this]() { return this->expired || this->tryToExpire; });// 检查定时器是否仍然有效  if (!expired) {// 执行任务  this->task();}}});// 分离线程,这样当线程结束时会自动释放资源  thread.detach();}void stop() {// 设置定时器到期标志  expired = true;tryToExpire = true;// 唤醒可能在等待的线程  cv.notify_all();// 如果线程是可连接的,则等待它结束  if (thread.joinable()) {thread.join();}}~Timer() {stop();}private:std::atomic<bool> expired;std::atomic<bool> tryToExpire;std::thread thread;std::function<void()> task;std::chrono::milliseconds interval;std::mutex mtx;std::condition_variable cv;
};// 使用示例  
int main() {Timer timer;timer.start(1000, []() {printf("timer task executed!\n");});std::this_thread::sleep_for(std::chrono::milliseconds(4200));printf("main thread waking up...\n");// 停止定时器  timer.stop();return 0;
}

上面代码的输出为:

timer task executed!
timer task executed!
timer task executed!
timer task executed!
main thread waking up...

上面代码中的 Timer 类使用一个内部线程来周期性地执行任务。当调用 start 方法时,它会启动一个线程,该线程将等待指定的时间间隔,然后执行任务。如果定时器正在运行,并且再次调用 start ,则会尝试停止当前线程并启动一个新的线程。调用 stop 方法会设置标志来通知线程退出循环,并结束执行。
注意:这个简单的定时器实现可能不适用于所有场景,特别是需要高精度或复杂调度的场景。对于更复杂的用例,可能需要使用专门的定时器库或考虑使用操作系统提供的定时器服务。
另外,这个实现中使用了 std::thread::detach 来分离线程。这意味着一旦线程完成执行,它会自动释放所有资源。然而,在某些情况下,使用 detach 可能会导致问题,因为它不允许检查线程是否已安全完成执行。在更复杂的应用程序中,使用 std::future 和 std::async 可能是更好的选择,因为它们提供了更好的异常处理和线程同步机制。

4 多线程搜索算法

在 C++ 中,可以使用多线程来加速搜索算法,特别是当处理大量数据或可以并行处理多个搜索任务时。以下是一个简单的例子,展示了如何使用 C++ 的多线程功能来加速一个简单的线性搜索算法。
假设有一个很大的整数数组,并且想要找到某个特定的值。可以将数组分成多个部分,并为每个部分分配一个线程来执行搜索。这样,搜索任务就可以并行执行,从而加速搜索过程。
如下为样例代码:

#include <iostream>  
#include <vector>  
#include <thread>  
#include <atomic>  // 搜索函数,用于单个线程  
bool searchInRange(const std::vector<int>& data, size_t start, size_t end, int target, std::atomic<bool>& found)
{for (size_t i = start; i < end; i++){if (data[i] == target){found = true;return true;}}return false;
}// 多线程搜索函数  
bool parallelSearch(const std::vector<int>& data, int target, size_t threadCount) 
{std::atomic<bool> found(false);const size_t rangeSize = data.size() / threadCount;std::vector<std::thread> threads;// 为每个线程分配一个搜索范围  for (size_t i = 0; i < threadCount; i++){size_t start = i * rangeSize;size_t end = (i == threadCount - 1) ? data.size() : start + rangeSize;threads.emplace_back(searchInRange, std::ref(data), start, end, target, std::ref(found));}// 等待所有线程完成  for (auto& thread : threads){thread.join();}// 检查是否找到了目标  return found;
}int main() 
{// 示例数据  std::vector<int> data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };int target = 6;// 使用多线程搜索  const size_t threadCount = std::thread::hardware_concurrency(); // 获取可用的CPU核心数  if (parallelSearch(data, target, threadCount)) {printf("found target %d in parallel search\n", target);}else {printf("target %d not found in parallel search\n", target);}return 0;
}

上面代码的输出为:

found target 6 in parallel search

在上面代码中, parallelSearch 函数负责创建多个线程,并将搜索任务分配给它们。每个线程都会调用 searchInRange 函数,该函数负责在分配给它的数组范围内搜索目标值。 std::atomic 类型的 found 变量用于跨线程同步搜索结果。
注意:这个简单的例子并没有考虑数据划分和线程同步的复杂性。在实际应用中,可能需要更复杂的策略来确保数据被均匀划分,并避免线程间的数据竞争。此外,对于某些类型的数据和搜索算法,多线程搜索可能并不会带来性能提升,甚至可能导致性能下降,因为线程创建和管理本身也需要资源。因此,在决定使用多线程之前,最好先分析数据和算法,看看它们是否适合并行处理。

相关文章:

突破编程_C++_高级教程(多线程编程实例)

1 生产者-消费者模型 生产者-消费者模型是一种多线程协作的设计模式&#xff0c;它主要用于处理生产数据和消费数据的过程。在这个模型中&#xff0c;存在两类线程&#xff1a;生产者线程和消费者线程。生产者线程负责生产数据&#xff0c;并将其放入一个共享的数据缓冲区&…...

精读《Function Component 入门》

1. 引言 如果你在使用 React 16&#xff0c;可以尝试 Function Component 风格&#xff0c;享受更大的灵活性。但在尝试之前&#xff0c;最好先阅读本文&#xff0c;对 Function Component 的思维模式有一个初步认识&#xff0c;防止因思维模式不同步造成的困扰。 2. 精读 什…...

类的构造方法

在类中&#xff0c;出成员方法外&#xff0c;还存在一种特殊类型的方法&#xff0c;那就是构造方法。构造方法是一个与类同名的方法&#xff0c;对象的创建就是通过构造方法完成的。每个类实例化一个对象时&#xff0c;类都会自动调用构造方法。 构造方法的特点&#xff1a; 构…...

ChatGPT和LLM

ChatGPT和LLM&#xff08;大型语言模型&#xff09;之间存在密切的关系。 首先&#xff0c;LLM是一个更为抽象的概念&#xff0c;它包含了各种自然语言处理任务中使用的各种深度学习模型结构。这些模型通过建立深层神经网络&#xff0c;根据已有的大量文本数据进行文本自动生成…...

「优选算法刷题」:判定字符是否唯一

一、题目 实现一个算法&#xff0c;确定一个字符串 s 的所有字符是否全都不同。 示例 1&#xff1a; 输入: s "leetcode" 输出: false 示例 2&#xff1a; 输入: s "abc" 输出: true限制&#xff1a; 0 < len(s) < 100 s[i]仅包含小写字母 二…...

详解自定义类型:枚举与联合体!

目录 ​编辑 一、枚举类型 1.枚举类型的声明 2.枚举类型的优点 3.枚举类型的使用 二、联合体类型(共用体&#xff09; 1.联合体类型的声明 2.联合体的特点 3.相同成员的结构体和联合体的对比 4.联合体大小的计算 5.用联合体判断大小端 三.完结散花 悟已往之不谏&…...

第13章 网络 Page738~741 13.8.3 TCP/UDP简述

libcurl是C语言写成的网络编程工具库&#xff0c;asio是C写的网络编程的基础类型库 libcurl只用于客户端&#xff0c;asio既可以写客户端&#xff0c;也可以写服务端 libcurl实现了HTTP\FTP等应用层协议&#xff0c;但asio却只实现了传输层TCP/UDP等协议。 在学习http时介绍…...

Tomcat要点总结

一、Tomcat 服务中部署 WEB 应用 1.什么是Web应用 &#xff08;1&#xff09; WEB 应用是多个 web 资源的集合。简单的说&#xff0c;可以把 web 应用理解为硬盘上的一个目录&#xff0c; 这个目录用于管理多个 web 资源。 &#xff08;2&#xff09;Web 应用通常也称之为…...

Ubuntu 20.04 安装RVM

RVM是管理Ruby版本的工具,使用RVM可以在单机上方便地管理多个Ruby版本。 下载安装脚本 首先使下载安装脚本 wget https://raw.githubusercontent.com/rvm/rvm/master/binscripts/rvm-installer 如果出现了 Connection refused 的情况, 可以考虑执行以下命令修改dns,再执…...

Ps:污点修复画笔工具

污点修复画笔工具 Spot Healing Brush Tool专门用于快速清除图像中的小瑕疵、污点、尘埃或其他不想要的小元素。 它通过分析被修复区域周围的内容&#xff0c;无需手动取样&#xff0c;自动选择最佳的修复区域来覆盖和融合这些不完美之处&#xff0c;从而实现无痕修复的效果。 …...

JAVA面试题17

什么是Java中的静态内部类&#xff1f;它与非静态内部类有什么区别&#xff1f; 答案&#xff1a;静态内部类是定义在另一个类中的类&#xff0c;并且被声明为静态。与非静态内部类不同&#xff0c;静态内部类不依赖于外部类的实例&#xff0c;可以直接访问外部类的静态成员。 …...

数据备份和恢复

数据备份和恢复 什么情况下会用到数据备份呢 数据丢失的场景 人为误操作造成的某些数据被误操作 软件BUG造成数据部分或者全部丢失 硬件故障造成数据库部分或全部丢失 安全漏洞被入侵数据恶意破坏 非数据丢失场景 基于某个时间点的数据恢复 开发测试环境数据库搭建 相同数据库的…...

核心篇 - 集成IS-IS配置实战

文章目录 一. 实验专题1.1. 实验1&#xff1a;配置单区域集成IS-IS1.1.1. 实验目的1.1.2. 实验拓扑1.1.3. 实验步骤&#xff08;1&#xff09;配置IP地址&#xff08;2&#xff09;配置IS-IS 1.1.4. 实验调试&#xff08;1&#xff09;查看邻接表&#xff08;2&#xff09;查看…...

【OpenAI Sora】开启未来:视频生成模型作为终极世界模拟器的突破之旅

这份技术报告主要关注两个方面&#xff1a;&#xff08;1&#xff09;我们的方法将各种类型的视觉数据转化为统一的表示形式&#xff0c;从而实现了大规模生成模型的训练&#xff1b;&#xff08;2&#xff09;对Sora的能力和局限性进行了定性评估。报告中不包含模型和实现细节…...

MVC 、DDD、中台、Java SPI(Service Provider Interface)

文章目录 引言I 单体架构DDD实现版本1.1 核心概念1.2 DDD四层架构规范1.3 案例1.4 请求转发流程II 领域服务调用2.1 菱形对称架构2.2 中台III Java SPI3.1 概念3.2 实现原理3.3 例子:本地SPI找服务see alsojava -cp</...

C++单例模式的实现

单例模式就是在整个程序运行期都只有一个实例。在代码实现方面&#xff0c;我们要限制new出多于一个对象这种情况的发生。而不是仅仅依靠无保障的约定。 目前大多数的编程语言的做法都是私有化构造函数&#xff0c;对外提供一个获取实例的接口。这样做的目的使实例的创建不能在…...

rust函数 stuct struct方法 关联函数

本文结合2个代码实例主要介绍了rust函数定义方法&#xff0c;struct结构体定义、struct方法及关联函数等相关基础知识。 代码1&#xff1a; main.rc #[derive(Debug)]//定义一个结构体 struct Ellipse {max_semi_axis: u32,min_semi_axis: u32, }fn main() {//椭圆&#xff0…...

浅谈基于中台模式的大数据生态体系的理解

这篇文章主要浅谈一下我对大数据生态体系建设的理解。 大数据生态系统为高并发&#xff0c;高吞吐&#xff0c;高峰值&#xff0c;高堆积等大规模数据的采集&#xff0c;处理&#xff0c;计算&#xff0c;存储&#xff0c;服务提供了完善的处理体系&#xff0c;致力于打造核心数…...

MySQL的锁机制

一&#xff1a;概述 锁是计算机协调多个进程或线程并发访问某一资源的机制&#xff08;避免争抢&#xff09;&#xff1b; 在数据库中&#xff0c;除传统的计算资源&#xff08;如CPU&#xff0c;RAM&#xff0c;I/O等&#xff09;的争用以外&#xff0c;数据也是一种供许多用…...

已解决ImportError: cannot import name ‘PILLOW_VERSION‘异常的正确解决方法,亲测有效!!!

已解决ImportError: cannot import name PILLOW_VERSION异常的正确解决方法&#xff0c;亲测有效&#xff01;&#xff01;&#xff01; 文章目录 问题分析 报错原因 解决思路 解决方法 总结 在Python项目开发中&#xff0c;依赖管理是保证项目正常运行的关键环节。然而&…...

力扣:300. 最长递增子序列

动态规划: 1. 先定义dp数组来表示在下标为i时最长递增子序列&#xff0c;先初始化一下每个下标的值为dp【i】1。同时我们要判断在下标i之前的最长的递增子序列为多少&#xff0c;在判断当前的下标i是否满足递增的条件满足的话就进行dp【i】的重新赋值。之后要更新接受的最长递…...

Swing程序设计(10)列表框,文本框,文本域,密码框

文章目录 前言一、列表框二、文本框&#xff08;域&#xff09; 1.文本框2.文本域三、密码框总结 前言 该篇文章简单介绍了Java中Swing组件里的列表框、文本框、密码框。 一、列表框 列表框&#xff08;JList&#xff09;相比下拉框&#xff0c;自身只是在窗体上占据固定的大小…...

【Java八股面试系列】JVM-常见参数设置

目录 堆内存相关 显式指定堆内存–Xms和-Xmx 显式新生代内存(Young Generation) 显式指定永久代/元空间的大小 垃圾收集相关 垃圾回收器 GC 日志记录 处理 OOM JDK监控和故障处理工具总结 堆内存相关 Java 虚拟机所管理的内存中最大的一块&#xff0c;Java 堆是所有线…...

【Python--Web应用框架大比较】

&#x1f680; 作者 &#xff1a;“码上有前” &#x1f680; 文章简介 &#xff1a;Python &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac; Django Django太重了&#xff0c;除了web框架&#xff0c;自带ORM和模板引擎&#xff0c;灵活和自由度不…...

Effective Objective-C 学习第三周

理解引用计数 Objective-C 使用引用计数来管理内存&#xff1a;每个对象都有个可以递增或递减的计数器。如果想使某个对象继续存活&#xff0c;那就递增其引用计数&#xff1a;用完了之后&#xff0c;就递减其计数。计数变为 0时&#xff0c;就可以把它销毁。 在ARC中&#xf…...

人工智能学习与实训笔记(四):神经网络之NLP基础—词向量

人工智能专栏文章汇总&#xff1a;人工智能学习专栏文章汇总-CSDN博客 本篇目录 四、自然语言处理 4.1 词向量 (Word Embedding) 4.1.1 词向量的生成过程 4.1.2 word2vec介绍 4.1.3 word2vec&#xff1a;skip-gram算法的实现 4.2 句向量 - 情感分析 4.2.1 LSTM (Long S…...

【教程】Kotlin语言学习笔记(一)——认识Kotlin(持续更新)

写在前面&#xff1a; 如果文章对你有帮助&#xff0c;记得点赞关注加收藏一波&#xff0c;利于以后需要的时候复习&#xff0c;多谢支持&#xff01; 【Kotlin语言学习】系列文章 第一章 《认识Kotlin》 文章目录 【Kotlin语言学习】系列文章一、Kotlin介绍二、学习路径 一、…...

MySQL性能分析1

1、查看执行频次 查看当前数据库的INSERT,UPDATE,DELETE,SELECT的访问频次&#xff0c;得到当前数据库是以插入&#xff0c;更新和删除为主还是以查询为主&#xff0c;如果是以插入&#xff0c;更新和删除为主的话&#xff0c;那么优化比重可以轻一点儿。 语法&#xff1a; …...

四、案例 - Oracle数据迁移至MySQL

Oracle数据迁移至MySQL 一、生成测试数据表和数据1.在Oracle创建数据表和数据2.在MySQL创建数据表 二、生成模板文件1.模板文件内容2.模板文件参数详解2.1 全局设置2.2 数据读取&#xff08;Reader&#xff09;2.3 数据写入&#xff08;Writer&#xff09;2.4 性能设置 三、案例…...

ABC340 A-F题解

文章目录 A题目AC Code&#xff1a; B题目AC Code&#xff1a; C题目AC Code&#xff1a; D题目AC Code&#xff1a; E题目思路做法时间复杂度AC Code&#xff1a; F题目思路AC Code&#xff1a; A 题目 模拟即可&#xff0c;会循环都能写。 AC Code&#xff1a; #include …...

微软 CMU - Tag-LLM:将通用大语言模型改用于专业领域

文章目录 一、前言二、主要内容三、总结 &#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 一、前言 论文地址&#xff1a;https://arxiv.org/abs/2402.05140 Github 地址&#xff1a;https://github.com/sjunhongshen/Tag-LLM 大语言模型&#xff08…...

Kafka集群安装与部署

集群规划 准备工作 安装 安装包下载&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1BtSiaf1ptLKdJiA36CyxJg?pwd6666 Kafka安装与配置 1、上传并解压安装包 tar -zxvf kafka_2.12-3.3.1.tgz -C /opt/moudle/2、修改解压后的文件名称 mv kafka_2.12-3.3.1/ kafka…...

C++初阶(十一) list

一、list的介绍及使用 1.1 list的介绍 list的文档介绍 1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。 2. list的底层是双向链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0c;在节点…...

图像卷积、步长、填充、特征图、多通道卷积、权重共享、感受野、池化

图像卷积、步长、填充、特征图、多通道卷积、权重共享、感受野、池化 卷积神经网络的一些基本概念&#xff1a;图像卷积、步长、填充、特征图、多通道卷积、权重共享、感受野、池化 1.图像卷积、步长、填充 图像卷积&#xff1a;卷积核矩阵在一个原始图像矩阵上 “从上往下、…...

CMake进行C/C++与汇编混合编程

1. 前提 这篇文章记录一下怎么用CMake进行项目管理, 并用C/C和汇编进行混合编程, 为了使用这项技术, 必须在VS的环境中安装好cmake组件 由于大部分人不会使用C/C与汇编进行混合编程的情况。所以这篇文章并不适用于绝大部分人不会对其中具体细节进行过多叙述。只是做一些简单的…...

缓存预热!真香

预热一般指缓存预热&#xff0c;一般用在高并发系统中&#xff0c;为了提升系统在高并发情况下的稳定性的一种手段。 缓存预热是指在系统启动之前或系统达到高峰期之前&#xff0c;通过预先将常用数据加载到缓存中&#xff0c;以提高缓存命中率和系统性能的过程。缓存预热的目…...

VS中设置#define _CRT_SECURE_NO_WARNINGS的原因和设置方式

原因&#xff1a; 在编译老的用C语言的开源项目的时候&#xff0c;可能因为一些老的.c文件使用了strcpy,scanf等不安全的函数&#xff0c;而报警告和错误&#xff0c;而导致无法编译通过。 解决方案&#xff1a; 我们有两种解决方案&#xff1a; 1、在指定的源文件的开头定…...

【网站项目】155在线考试与学习交流网页平台

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…...

解决IDEA的Project无法正常显示的问题

一、问题描述 打开IDEA&#xff0c;结果发现项目结构显示有问题&#xff1a; 二、解决办法 File -> Project Structure… -> Project Settings (选Modules)&#xff0c;然后导入Module 结果&#xff1a; 补充&#xff1a; IDEA提示“The imported module settings a…...

CDF和PDF的比较

以下内容来自ChatGPT&#xff0c;科技改变生活 Cumulative Distribution Function (CDF)&#xff08;累积分布函数&#xff09;和 Probability Density Function (PDF)&#xff08;概率密度函数&#xff09;是统计学和概率论中两个重要的概念&#xff0c;用于描述随机变量的性…...

编译基本过程 预处理器

编译基本过程 源代码(main.c)->预处理器(cpp)->编译器(gcc/clang/msvc)->汇编器(as)->链接器(ld)->可执行文件(main.exe) 预处理器 C语言中预处理器&#xff1a;执行预处理命令(文件包含、宏替换、条件编译)处理注释(将所有注释替换为空格)处理续行符(将所有…...

模拟算法.

1.什么是模拟 在信息奥赛中,有一类问题是模拟一个游戏的对弈过程或者模拟一项任务的操作过程.比如乒乓球在比赛中模拟统计记分最终判断输赢的过程等等,这些问题通常很难通过建立数学模型用特定的算法来解决因为它没有一种固定的解法,需要深刻理解出题者对过程的解释一般只能采…...

ClickHouse--10--临时表、视图、向表中导入导出数据

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 1.临时表1.1 特征1.2 创建一个临时表 2.视图2.1 普通视图2.2 物化视图 3.向表中导入导出数据3.1 案例 1.临时表 1.1 特征 ClickHouse 支持临时表&#xff0c;临时表…...

Python一些可能用的到的函数系列124 GlobalFunc

说明 GlobalFunc是算网的下一代核心数据处理基础。 算网是一个分布式网络&#xff0c;为了能够实现真的分布式计算&#xff08;加快大规模任务执行效率&#xff09;&#xff0c;以及能够在很长的时间内维护不同版本的计算方法&#xff0c;需要这样一个对象/服务来支撑。Globa…...

python中线程/线程池,进程/进程池的创建

创建子线程 # 创建子线程t1 threading.Thread(targetjob,args(1,))# 执行子线程t1.start()# 等待子线程执行print("waiting threading")t1.join()print("threading done")创建子进程 # 创建子进程p1 multiprocessing.Process(targetjob,args(1,),name&qu…...

【c++】vector的增删查改

1.先定义一个类对象vector 为了防止和库里面发生冲突&#xff0c;定义一个命名空间&#xff0c;将类对象放在命名空间 里面 #include<iostream> using namespace std; namespace zjw {class vector {public:private:}; }2.定义变量&#xff0c;需要一个迭代器&#xff…...

【研究生复试】计算机软件工程人工智能研究生复试——资料整理(速记版)——JAVA

1、JAVA 2、计算机网络 3、计算机体系结构 4、数据库 5、计算机租场原理 6、软件工程 7、大数据 8、英文 自我介绍 1. Java 1. 和 equals的区别 比较基本数据类型是比较的值&#xff0c;引用数据类型是比较两个是不是同一个对象&#xff0c;也就是引用是否指向同 一个对象&…...

JVM-JVM中对象的生命周期

申明&#xff1a;文章内容是本人学习极客时间课程所写&#xff0c;文字和图片基本来源于课程资料&#xff0c;在某些地方会插入一点自己的理解&#xff0c;未用于商业用途&#xff0c;侵删。 原资料地址&#xff1a;课程资料 对象的创建 常量池检查:检查new指令是否能在常量池…...

RegExp正则表达式左限定右限定左右限定,预查询,预查寻,断言 : (?<= , (?= , (?<! , (?!

RegExp正则表达式左限定右限定左右限定,预查询,预查寻,断言 : (?< , (? , (?<! , (?! 有好多种称呼 (?< , (? , (?<! , (?! 有好多种称呼 , 我称为: 左限定, 右限定, 左否定, 右否定 (?<左限定)    (?右限定)(?<!左否定)    (?!右限定) 再…...

相机图像质量研究(30)常见问题总结:图像处理对成像的影响--重影

系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结&#xff1a;光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结&#xff1a;光学结构对成…...