【c++11线程库的使用】

#include<iostream>
#include<thread>
#include<string>
using namespace std;
void hello(string msg) {
    for (int i = 0; i < 1000; i++) {
        cout << i;
        cout << endl;
    }
}
int main() {
    //1.创建线程
    thread thread1(hello,"hello Thread");//打印的就是hello Thread
    //主程序等待  看我这个程序运行完了吗
    //第三个知识点:thread1.join();//当上一个线程没有结束之前主线程是不会结束的
    //主线程结束之后我的线程依然可以再后台运行 那就是分离线程detach()
    //第四个知识点:thread1.detach();//这个就是主线程和子线程分离
    //第五个知识点:
    bool isJoin = thread1.joinable();//针对一个线程,可以调用detach,或者join。两者是互斥的关系,
    //也就说一旦调用了join,detach就不能再调用了,反之亦成立。
    //判断是否已经使用过join或者detach可以用joinable。
    if (isJoin) {
        thread1.join();
    }
    cout << "over" << endl;
    return 0;
}

std::ref传递引用类型

 #include<iostream>
 #include<thread>
 #include<string>
 using namespace std;
 void foo(int& x) {
     x += 1;
     
 }
 int main() {
     int a = 1;
     //thread t(foo, 1);//这个1不是引用是临时变量穿不进去  就释放掉了
     //所以要用ref  修饰这个a证明这个a就是它需要的引用变量
     thread t(foo, ref(a));
     t.join();
     cout << a << endl;
     return 0;
 }

类成员函数作为入口函数,类对象被提前释放

  错误示例:
 #include <iostream>
 #include <thread>
 ​
 class MyClass {
 public:
     void func() {
         std::cout << "Thread " << std::this_thread::get_id() 
         << " started" << std::endl;
         // do some work
         std::cout << "Thread " << std::this_thread::get_id() //线程的信息
         << " finished" << std::endl;
     }
 };
 ​
 int main() {
     MyClass obj;
     std::thread t(&MyClass::func, &obj);
     // obj 被提前销毁了,会导致未定义的行为
     return 0;
 }

上面的代码中,在创建线程之后,obj 对象立即被销毁了,这会导致在线程执行时无法访问 obj 对象,可能会导致程序崩溃或者产生未定义的行为。

为了避免这个问题,可以使用 std::shared_ptr 来管理类对象的生命周期,确保在线程执行期间对象不会被销毁。具体来说,可以在创建线程之前,将类对象的指针封装在一个 std::shared_ptr 对象中,并将其作为参数传递给线程。这样,在线程执行期间,即使类对象的所有者释放了其所有权,std::shared_ptr 仍然会保持对象的生命周期,直到线程结束。

 #include<iostream>
 #include<thread>
 #include<string>
 #include<memory>
 using namespace std;
 class A {
 public:
     void foo() {
         cout << "hello world" << endl;;
     }
 };
 int main() {
     shared_ptr<A> a = make_shared<A>();//这个指针就是一直有效的
     //如果使用指针就还得手动释放内存  就很麻烦所以用智能指针
     //不需要这个变量的时候就释放掉
     thread t(&A::foo, a); // 这个指针就是一直有效的
     
     t.join();
     return 0;
 }

互斥量解决多线程数据共享问题

数据共享问题分析

在多个线程中共享数据时,需要注意线程安全问题。如果多个线程同时访问同一个变量,并且其中至少有一个线程对该变量进行了写操作,那么就会出现数据竞争问题。数据竞争可能会导致程序崩溃、产生未定义的结果,或者得到错误的结果。

为了避免数据竞争问题,需要使用同步机制来确保多个线程之间对共享数据的访问是安全的。常见的同步机制包括互斥量、条件变量、原子操作等。

 //这个就是互相抢夺资源的代码
 #include<iostream>
 #include<thread>
 #include<string>
 #include<memory>
 using namespace std;
 int a = 0;
 void foo() {
     for (int i = 0; i <= 10000; i++) {
         a += 1;
     }
 }
 int main() {
     thread t1(foo);
     thread t2(foo);
 ​
     t1.join();
     t2.join();
     cout << a << endl;//输出17520  预期结果是20000 这就是互相抢夺资源了
     return 0;
 }

互斥锁解决

 #include<iostream>
 #include<thread>
 #include<string>
 #include<memory>
 #include<mutex>
 using namespace std;
 int a = 0;
 mutex mtx;
 void foo() {
     for (int i = 0; i < 10000; i++) {
         mtx.lock();//lock枷锁操作 如果不解锁其它线程是不能执行下面操作的
         a += 1;
         mtx.unlock();
     }
 }
 int main() {
     thread t1(foo);
     thread t2(foo);
 ​
     t1.join();
     t2.join();
     cout << a << endl;//输出是20000 
     return 0;
 }

互斥量死锁

 //这就是死锁  死锁就是func_1在等着m2的所有权  func_2在等着m1的所有权  但是他俩都再互相等所以就没有释放彼此的锁就产生了死锁
 #include<iostream>
 #include<thread>
 #include<mutex>
 using namespace std;
 ​
 mutex m1,m2;
 void func_1() {
     for (int i = 0; i < 50; i++) {
         m1.lock();
         m2.lock();
         m1.unlock();
         m2.unlock();
 ​
     }
     
 }
 void func_2() {
     for (int i = 0; i < 50; i++) {
         m2.lock();
         m1.lock();
         m2.unlock();
         m1.unlock();
 ​
     }
     
 }
 int main() {
     thread t1(func_1);
     thread t2(func_2);
 ​
     t1.join();
     t2.join();
     cout << "over" << endl;
     return 0;
 }
lock_guard 与 std::unique_lock

lock_guard的特点:

  • 当构造函数被调用时,该互斥锁会被自动锁定

  • 当析构函数被调用时,该互斥锁会被自动解锁

  • lock_guard对象不能复制或移动,因此只能在局部作用域中使用

 #include<iostream>
 #include<thread>
 #include<string>
 #include<memory>
 #include<mutex>
 using namespace std;
 int a = 0;
 mutex mtx;
 void foo() {
     for (int i = 0; i < 10000; i++) {
         lock_guard<mutex>lg(mtx);
         a += 1;
     }
 }
 int main() {
     thread t1(foo);
     thread t2(foo);
 ​
     t1.join();
     t2.join();
     cout << a << endl;//输出20000 
     return 0;
 }

unique_lock:

std:: 是 C++ 标准库中提供的一个互斥量封装类,用于在多线程程序中对互斥量进行加锁和解锁操作。它的主要特点是可以对互斥量进行更加灵活的管理,包括延迟加锁、条件变量、超时等。

std::unique_lock 提供了以下几个成员函数:

 `lock()`:尝试对互斥量进行加锁操作,如果当前互斥量已经被其他线程持有,则当前线程会被阻塞,直到互斥量被成功加锁。
 ​
 `try_lock()`:尝试对互斥量进行加锁操作,如果当前互斥量已经被其他线程持有,则函数立即返回 `false`,否则返回 `true`。
 ​
 `try_lock_for(const std::chrono::duration<Rep, Period>& rel_time)`:尝试对互斥量进行加锁操作,如果当前互斥量已经被其他线程持有,则当前线程会被阻塞,直到互斥量被成功加锁,或者超过了指定的时间。
 ​
 `try_lock_until(const std::chrono::time_point<Clock, Duration>& abs_time)`:尝试对互斥量进行加锁操作,如果当前互斥量已经被其他线程持有,则当前线程会被阻塞,直到互斥量被成功加锁,或者超过了指定的时间点。
 ​
 `unlock()`:对互斥量进行解锁操作。

单例设计模式

单例设计模式是一种常见的设计模式,用于确保某个类只能创建一个实例。由于单例实例是全局唯一的,因此在多线程环境中使用单例模式时,需要考虑线程安全的问题。

下面是一个简单的单例模式的实现:

 #include<iostream>
 #include<thread>
 #include<string>
 #include<memory>
 #include<mutex>
 using namespace std;
 ​
 //日志功能就是单例模式 
 class Log {
 public:
     Log() {};
     Log(const Log& log) = delete;//在某些情况下,我们希望类的实例只能有一个。通过禁用拷贝构造函数,我们可以确保不会创建多个相同的实例。
     Log& operator = (const Log& log) = delete; 
 ​
     static Log& GetInstance() {//这样写就是全局只有一个log不会有第二个这就是懒汉模式
         //提前构造了一个对象 需要的时候就返回这个对象
         static Log log;
         return log;
         /*
         * static Log *log = nullptr;//饿汉模式
         * if(!log) log = new Log;
         * return *log;//提前不声明指针,需要的时候才创建对象
         */
     }
     void PrintLog(string msg) {
         cout << msg << endl;
 ​
     }
 };
 int main() {
     Log::GetInstance().PrintLog("error");
         return 0;
 }

call_once知识

多线程在单例模式中出现的一个问题:就是一下代码因为是单例模式但是线程调用两个线程是并行的 那就声明了两次

 ​
 #include<iostream>
 #include<thread>
 #include<string>
 #include<memory>
 #include<mutex>
 using namespace std;
 ​
 //日志功能就是单例模式 
 class Log {
 public:
     Log() {};
     Log(const Log& log) = delete;
     Log& operator = (const Log& log) = delete; 
 ​
     static Log& GetInstance() {//这样写就是全局只有一个log不会有第二个这就是懒汉模式
         //提前构造了一个对象 需要的时候就返回这个对象
         static Log log;
         return log;
         /*
         * static Log *log = nullptr;//饿汉模式
         * if(!log) log = new Log;
         * return *log;//提前不声明指针,需要的时候才创建对象
         */
     }
     void PrintLog(string msg) {
         cout << msg << endl;
 ​
     }
 };
 void printError() {
     
     Log::GetInstance().PrintLog("error");
 }
 int main() {
     thread t1(printError);
     thread t2(printError);
     t1.join();
     t2.join();
 ​
         return 0;
 }

我们就用call_once解决

 #include<iostream>
 #include<thread>
 #include<string>
 #include<memory>
 #include<mutex>
 using namespace std;
 ​
 //日志功能就是单例模式 
 static Log* log = nullptr;//饿汉模式
 static once_flag once;
 class Log {
 public:
     Log() {};
     Log(const Log& log) = delete;
     Log& operator = (const Log& log) = delete; 
 ​
     static Log& GetInstance() {//这样写就是全局只有一个log不会有第二个这就是懒汉模式
         提前构造了一个对象 需要的时候就返回这个对象
         //static Log log;
         //return log;
         
         if(!log) log = new Log;
         call_once(once, init);
         return *log;//提前不声明指针,需要的时候才创建对象
         
     }
     void PrintLog(string msg) {
         cout << msg << endl;
 ​
     }
     static void init() {
         if (!log) log = new Log;
     }   
 };
 void printError() {
     
     Log::GetInstance().PrintLog("error");
 }
 int main() {
     thread t1(printError);
     thread t2(printError);
     t1.join();
     t2.join();
 ​
         return 0;
 }

线程池的使用 写的

必要的东西:

- 一个线程数组(维护一堆线程)
- 任务队列
- 用户 /生产者向队列里加任务
- 线程数组派线程取任务

```
#include<iostream>
#include<thread>
#include<string>
#include<memory>
#include<mutex>
#include<condition_variable>
#include<queue>
#include<vector>
using namespace std;
#include<functional>
class ThreadPool {
public:
	ThreadPool(int numThreads) :stop(false) {
		for (int i = 0; i < numThreads; i++) {
			threads.emplace_back([this] {//添加任务  用了拉姆达表达式
				while (1) {
					unique_lock<mutex> lock(mtx);
					condition.wait(lock, [this] {//判断任务队列里面有没有任务 有就取
						return !tasks.empty() || stop;
						});
					if (stop && tasks.empty()) {
						return;
					}
					function<void()> task(move(tasks.front()));    
                    tasks.pop();
					lock.unlock();
					task();
				}
				});
		}
	}
	~ThreadPool() {
		{
			unique_lock<mutex> lock(mtx);
			stop = true;//代表线程结束了
		}
		condition.notify_all();
		for (auto& t : threads) {//自动类型推导 自动判断这个数组里面是什么类型
			t.join();
		}
	}
	template<class F,class... Args>
	void enqueue(F &&f,Args&&... args) {
		function<void()> task =
			bind(forward<F>(f),
			forward<Args>(args)...);
		{
			unique_lock<mutex> lock(mtx);
			tasks.emplace(move(task));
		}
		condition.notify_one();
		

	}
private:
	vector<thread> threads;//线程数组
	queue<function<void()>> tasks;

	mutex mtx;
	condition_variable condition;//条件变量
	bool stop;//线程池什么时候终止
 };
int main() {
	ThreadPool pool(4);
	for (int i = 0; i < 10; i++) {
		pool.enqueue([i]() {
			cout << "第几个任务" << i << "任务开始" << endl;
			this_thread::sleep_for(chrono::seconds(1));
			cout << "第几个任务" << i << "任务结束" << endl;
			});
	}
	return 0;
	 
}

```

相关推荐

  1. c++11线使用

    2024-03-11 12:58:02       17 阅读
  2. C++11 Thead线基本使用

    2024-03-11 12:58:02       34 阅读
  3. C/C++ 16C++11线

    2024-03-11 12:58:02       31 阅读
  4. 并发编程2-掌握C#线使用

    2024-03-11 12:58:02       20 阅读
  5. C++】学习记录--Thread线使用

    2024-03-11 12:58:02       18 阅读
  6. 【2】c++多线技术之多线标准使用

    2024-03-11 12:58:02       14 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-11 12:58:02       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-11 12:58:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-11 12:58:02       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-11 12:58:02       20 阅读

热门阅读

  1. 数据结构-双链表

    2024-03-11 12:58:02       21 阅读
  2. 如何在vue中使用sort对数组进行排序

    2024-03-11 12:58:02       20 阅读
  3. TensorFlow是啥

    2024-03-11 12:58:02       20 阅读
  4. 2024 年 AI 辅助研发趋势

    2024-03-11 12:58:02       24 阅读
  5. 为什么不推荐使用外键

    2024-03-11 12:58:02       27 阅读
  6. vite项目修改依赖不更新,清除依赖缓存

    2024-03-11 12:58:02       17 阅读
  7. linux 入门(七)-ffmpeg使用

    2024-03-11 12:58:02       20 阅读
  8. 单调栈(经典例题)找最近的小数

    2024-03-11 12:58:02       21 阅读