申请个人网站和企业官网有什么不同,线上网站建设,semi,现在哪个公司的网络比较好提示#xff1a;文章 文章目录
前言一、背景二、 2.1 2.2 总结
前言
前期疑问#xff1a; 本文目标#xff1a; 一、背景
来源于b站视频
C11 多线程编程-小白零基础到手撕线程池
学习来源#xff1a;https://www.bilibili.com/video/BV1d841117SH/?p2spm_id_f… 提示文章 文章目录
前言一、背景二、 2.1 2.2 总结
前言
前期疑问 本文目标 一、背景
来源于b站视频
C11 多线程编程-小白零基础到手撕线程池
学习来源https://www.bilibili.com/video/BV1d841117SH/?p2spm_id_frompageDrivervd_source1a54eaaaa0e36b9ef70e2dbe59d5b137
http://www.seestudy.cn/?list_9/35.html
相关知识点
thread创建线程joindetachjoinablestd::ref智能指针在头文件中
二 、互斥量
自己写的代码
2.1 互斥量
#include iostream
#include threadint counter 0;void fun()
{for (int i 0; i 10000; i){counter;}
}int main() {std::thread t1(fun);std::thread t2(fun);t1.join();t2.join();std::cout a: counter std::endl;
}没有实现竞争访问a导致结果不是2000的情况我这边的打印结果是20000。
看有的评论说加一个0实现了我加了好几个0还是没有实现。
有的评论说现在编译器都是2000了不明所以继续向下看吧。 第二天来用了课程对应的代码重新跑了一下同时循环次数尝试加了个0。又出现了变量打印的值不是20000的情况。而我的myFirstTest工程依然出现不了预期的情况先不管了。
2.2 互斥量锁死
#include iostream
#include thread
#include mutexstd::mutex m1, m2;int counter 0;//死锁
void fun1()
{for (int i 0; i 5000; i){m1.lock();m2.lock();m1.unlock();m2.unlock();}
}void fun2()
{for (int i 0; i 5000; i){m2.lock();m1.lock();m1.unlock();m2.unlock();}
}void fun()
{for (int i 0; i 1000000; i){counter;}
}int main() {std::thread s1(fun1);std::thread s2(fun2);s1.join();s2.join();std::thread t1(fun);std::thread t2(fun);t1.join();t2.join();std::cout a: counter std::endl;
}循环次数50、500都不会锁死5000会锁死。
视频中给出的防止死锁的解决办法是每个线程都先获取m1继续获取m2。顺序获取。可以防止死锁。
2024年9月29日17:11:45 今天再看代码理解之前写的【顺序获取。可以防止死锁。】应该是修改上述代码为都先获取m1再获取m2
5、std::lock_guard
#include iostream
#include thread
#include mutexstd::mutex tex;
std::mutex m1, m2;int counter 0;void fun()
{for (int i 0; i 1000000; i){std::lock_guardstd::mutex lg(tex);counter;}
}int main() {std::thread t1(fun);std::thread t2(fun);t1.join();t2.join();std::cout a: counter std::endl;
}
lock_guard源码
templateclass _Mutexclass lock_guard{ // class with destructor that unlocks a mutex
public:using mutex_type _Mutex;explicit lock_guard(_Mutex _Mtx): _MyMutex(_Mtx){ // construct and lock_MyMutex.lock();}lock_guard(_Mutex _Mtx, adopt_lock_t): _MyMutex(_Mtx){ // construct but dont lock}~lock_guard() noexcept{ // unlock_MyMutex.unlock();}lock_guard(const lock_guard) delete;lock_guard operator(const lock_guard) delete;
private:_Mutex _MyMutex;};针对这个源码其中_MyMutex是私有成员变量。explicit表示禁止隐式转换。还涉及到构造函数重载、禁用构造、禁用拷贝函数。
2024年9月29日17:21:30
这篇文章是在之前写的后面我又因为看代码疑惑信号量的使用又写了下面的一篇文章关于多线程unique_lock和guard_lock而实际我在写关于多线程unique_lock和guard_lock这篇文章的时候也没有想起这边写的关于信号量的知识点。然后这次在看到这篇文章就想到了后面写的文章关于多线程unique_lock和guard_lock。但是我也忘了关于多线程unique_lock和guard_lock这篇文章的内容了。所以两篇文章结合看了下加深了理解。
… …
七、 std::call_once与其使用场景
涉及到单例类。两种模式
饿汉模式和懒汉模式。
教程中以log类举例子
static Log GetInstance()
{static Log log;return log;
}
//这种是饿汉模式构建类的时候就创建Log静态类对象。
//然后我的疑问是每次GetInstance的时候不会多次创建log对象吗实际是log是静态成员只有一个下面的是懒汉对象
static Log GetInstance()
{static Log *log nullptr;if(!log) {log new Log();}return log;
}上面代码我的疑问是每次GetInstance的时候不会多次new对象吗实际是不会因为if(!log)做了判断log不为空就不会再new对象。
针对饿汉模式在构造函数中创建静态变量这边为什么不会继续创建对象我查了资料没看到啥解释。
但是看到另外两个点。
第一个就是懒汉模式申请的堆内存如果释放会内存泄漏。我觉得可以在析构函数释放就可以。
第二个就是帖子提到单例类模式线程不安全主要是懒汉模式多个线程读取if(!log)中log变量的时候可能会多次申请堆内存。可以在if(!log)增加互斥锁但是会影响效率。
参考文档https://blog.csdn.net/code_feien/article/details/110423021
针对上述静态变量的问题我还写了下面的测试代码
#include iostreamusing namespace std;int getData()
{static int test 0;test;return test;
}int main() {std::cout Hello, World! std::endl;for(int i 0; i 10; i){int num getData();cout num endl;}return 0;
}//预测结果是10
//实际打印结果1——10,确实和预期一样
//码可以理解为静态变量已经创建生命周期一直存在直到程序结束九、 线程池
threadPool.h文件
#include iostream
#include thread
#include mutex
#include condition_variable
#include functional
#include queueclass ThreadPool {
public:ThreadPool(int numThreads) : stop(false) {for (int i 0; i numThreads; i) {threads.emplace_back([this] {while (true) {std::unique_lockstd::mutex lock(mutex);condition.wait(lock, [this] { return stop || !tasks.empty(); });if (stop tasks.empty()) {return;}std::functionvoid() task(std::move(tasks.front()));tasks.pop();lock.unlock();task();}});}}~ThreadPool() {{std::unique_lockstd::mutex lock(mutex);stop true;}condition.notify_all();for (std::thread thread : threads) {thread.join();}}templatetypename F, typename... Argsvoid enqueue(F f, Args... args) {std::functionvoid() task(std::bind(std::forwardF(f), std::forwardArgs(args)...));{std::unique_lockstd::mutex lock(mutex);tasks.emplace(std::move(task));}condition.notify_one();}private:std::vectorstd::thread threads;std::queuestd::functionvoid() tasks;std::mutex mutex;std::condition_variable condition;bool stop;
};main.cpp
#include threadPool.hint main(void)
{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;
}关于上述代码设计到lamda等知识点
9.1 std:bind
其中关于std::bind见这篇文章: std::bind的讲解
9.2 std::forward
然后还有一个知识点就是std::forward
参考这篇文章std::forward与完美转发详解
然后我继续查找关于std::forward知识点看到这个例子 std::forward入门 std::forward 是 C11 引入的标准库函数用于实现完美转发。完美转发意味着在函数模板内保持传递给函数的参数的左右值属性和常量属性。 下面是一个简单的 std::forward 使用示例 #include iostream
#include utility// 这是一个函数模板用来展示完美转发的效果
templatetypename T
void printValue(T val) {// 使用std::forward保留val的左右值属性和常量属性std::cout (std::is_sameT, int::value ? LValue: : RValue: ) std::forwardT(val) std::endl;
}int main() {int a 5;printValue(a); // 将a作为左值传递printValue(std::move(a)); // 将a转换为右值并传递return 0;
}在这个例子中printValue 是一个函数模板它接受一个模板参数可以是任何类型的右值引用。当我们调用 printValue 时我们可以传递一个左值或者右值。std::forward 保证在函数内部我们传入的参数在模板实例化时保持其原有的左右值属性和常量属性。
输出结果将会是
LValue: 5
RValue: 5上述我不知道为什么上面的例子就能表现出std::forward的完美转发我目前理解为std::forward可以实现左值和右值的转发。
然后上面有涉及到一个点就是std::is_same关于std::is_same参考这篇文章C 语言 std::is_same
9.3 std::function
std::function详解
我在纠结**std::functionvoid() task(std::move(tasks.front()));**这个std::functionvoid()是什么意思。查到下面的一个文章
c std::function的使用
其中一个示例
#include functionalvoid function1()
{std::cout This is function1. std::endl;
}int main()
{// 使用函数指针初始化 std::function 对象std::functionvoid() f1 function1;f1();
}//打印结果
//This is function1.我理解大概意思就是std::functionvoid()就是可以接收一个void f()函数。
9.4 对这个线程池的理解
lamda写法不是很理解一直想找一个c写的线程池代码没找到。
看了b站陈子青的视频使用了他写的线程池即有很多lamda表达式的线程池代码。可以实现900多个文件的正常读写。 总结
未完待续