9.30学习记录(补)

手撕线程池:

1.进程:进程就是运行中的程序

2.线程的最大数量取决于CPU的核数

3.创建线程 thread t1;

在使用多线程时,由于线程是由上至下走的,所以主程序要等待线程全部执行完才能结束否则就会发生报错。通过thread.join()来实现

但是如果在一个比较严谨的项目中,我们最好在使用join函数的时候先判断一下是不是可以使用join函数,可以通过joinable函数来判断 它返回的是一个bool值。

join函数是阻塞的,当线程函数没有执行完时,整个程序是卡在join函数的位置。

线程函数可以传参数,如下图所示:

4.在使用多线程时,要注意几个问题:

线程函数中数据未定义的错误

1.传递临时变量的问题:

在传递参数时如果参数是临时变量,那么这个程序会报错,是无法编译的。所以 我们可以使用ref函数来传递引用类型,这样程序就可以正常运行了。

2.传递指针或引用指向局部变量的问题

如下图所示: a是一个局部的变量,它只在test上是有效的,存放在栈区,如果test结束调用return(void 可以隐藏return)a变量的地址就被释放掉了,那么线程函数无法取到a的引用,所以会报错,解决方法就是将a定义成全局变量。

3.传递指针或引用指向已经释放的内存的问题

4.类对象可能被释放掉,

所以可以引出智能指针share_ptr来管理这个类的对象,防止因为某些原因将对象a释放掉导致地址a的丢失, 通过引入指针可以,但是最后还是要记得释放指针所占用的内存,所以引出了智能指针。

5.如何确定你的线程是线程安全的?

如果多线程的程序每次运行结果与单线程运行结果始终是一样的,那么你的线程就是线程安全的

6.互斥量死锁

两个线程相互等待导致的死锁

解决方法就是改变一下获取锁的顺序就好了。

让先获取m的线程m和m2都获取,这样就不会出现互相等待的情况了。

7.lock_guard

lock_guard是C++标准库中的一种互斥量封装类,用于保护共享数据,防止多个线程同时访问同一资源而导致的数据竞争的问题。特点如下:

1.当构造函数被调用时,该互斥量会被自动锁定。

2.当析构函数被调用时,该互斥量会被自动解锁。

std::lock_guard对象不能复制或移动,因此他只能在局部作用域中使用。因为在源码中他禁用了拷贝构造和等号。

8.unique_lock

用于管理互斥量(mutex)的锁定和解锁操作。它提供了比直接使用std::lock_guard更灵活的互斥量管理方式。

函数try_lock_for()可以等待一段时间,正常的锁是一直等待的,而这个unique_lock可以支持,时间到之后就不等了,直接返回掉。

9.std::call_once

它是 C++ 标准库中的一个函数模板,用于保证某个函数在多线程环境下仅被调用一次。它与懒汉模式的单例实现等场景密切相关,有助于解决多线程中初始化资源时可能出现的多次初始化问题。

10.实现生产者消费者模型:

11.线程池:

emplace_ back和push_back()区别;

push_back()会执行一个拷贝构造,它会创建一个临时对象,然后将这个临时对象复制或者移动到容器的末尾。

emplace_back直接调用构造函数,它直接在容器的末尾就地构造元素,避免了创建临时对象的开销(如果构造函数的参数可以直接用来初始化对象的话)。

cpp 复制代码
#include<iostream>
#include<thread>
#include<functional>
#include<mutex>
#include<condition_variable>
#include<vector>
#include<queue>

class ThreadPool {
public:
	ThreadPool(int numThreads) :stop(false) {

		for (int i = 0; i < numThreads; i++) {
			threads.emplace_back([this] {//创建并添加一个新的线程。每个线程执行一个 lambda 函数
				while (true) {
					std::unique_lock<std::mutex> lock(mtx);
					condition.wait(lock, [this] { return stop || !tasks.empty(); });//使用lambda表达式 如果为false 为空 阻塞在这里
					//如果为true 代表任务队列不为空 就往下运行
					if (stop && tasks.empty()) {
						return;
					}
					std::function<void()> task(std::move(tasks.front()));
					tasks.pop();
					lock.unlock();
					task();
				}
				});


		}
	}
	~ThreadPool() {
		{
			std::unique_lock<std::mutex> lock(mtx);
			stop = true;
		}
		condition.notify_all();
		for (std::thread& thread : threads) {
			thread.join();
		}
	}
	template<typename F, typename... Args>
	void enqueue (F&& f, Args&&... args){//...是可变参数包的语法标记,表示Args可以接受零个或多个类型参数
		std::function<void()> task(std::bind(std::forward<F>(f), std::forward<Args...>));
		{
			std::unique_lock<std::mutex> lock(mtx);
			tasks.emplace(std::move(task));

		}
		condition.notify_one();
	}
private:
	std::vector<std::thread> threads;
	std::queue<std::function<void()>> tasks;
	std::mutex mtx;
	std::condition_variable condition;
	bool stop;
};
int main() {
	ThreadPool pool(4);
	for (int i = 0; i < 8; ++i) {
		pool.enqueue([i] {
			std::cout << "Task " << i << " is running in thread " << std::this_thread::get_id() << std::endl;
			std::this_thread::sleep_for(std::chrono::seconds(1));
			std::cout << "Task " << i << " is done" << std::endl;
			});
	}
	return 0;
}

12.字符串转整形

cpp 复制代码
#include<iostream>
#include<string>

int StrToInt(char* str) {//字符串转整形
	int number=0;

	while (*str!=0) {
		number = number * 10 + *str - '0';
		++str;
	}
	return number;
}

int main() {

	char* ptr = new char[6];//分配能容纳6个字符(包括'\0')的内存
	strcpy_s(ptr,6,"world");
	std::cout<<StrToInt(ptr);

}
相关推荐
西岸行者4 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意4 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码4 天前
嵌入式学习路线
学习
毛小茛4 天前
计算机系统概论——校验码
学习
babe小鑫4 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms4 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下4 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。4 天前
2026.2.25监控学习
学习
im_AMBER4 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J4 天前
从“Hello World“ 开始 C++
c语言·c++·学习