IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++11多线程2 -> 正文阅读

[C++知识库]C++11多线程2

参考博客:https://blog.csdn.net/qq_38231713/category_10001159.html

一、单例设计模式下多线的内存共享

1、单例设计模式

????????在整个项目中某些类只允许存在一个对象,这种类被称为单例类

? ? ? ? 类的构造函数是私有的,通过静态成员函数进行实例化

2、单例设计模式中的共享数据

在多个线程中都需要使用这个对象,都需要进行构造,但是只有在这个对象为NULL的时候才能进行构造,所以需要使用lock_guard进行保护,而为了提高效率,使用了双重锁定的判断方式

#include <iostream>	
#include <mutex>
using namespace	std;

mutex myMutex;
//懒汉模式
class Singleton
{
public:
	static Singleton * getInstance() {
         //双重锁定 提高效率
		if (instance == NULL) {
			lock_guard<mutex> myLockGua(myMutex);
			if (instance == NULL) {
				instance = new Singleton;
			}
		}
		return instance;
	}
private:
	Singleton() {}
	static Singleton *instance;
};
Singleton * Singleton::instance = NULL;

//饿汉模式
class Singleton2 {
public:
	static Singleton2* getInstance() {
		return instance;
	}
private:
	Singleton2() {}
	static Singleton2 * instance;
};
Singleton2 * Singleton2::instance = new Singleton2;

int main(void)
{
	Singleton * singer = Singleton::getInstance();
	Singleton * singer2 = Singleton::getInstance();
	if (singer == singer2)
		cout << "二者是同一个实例" << endl;
	else
		cout << "二者不是同一个实例" << endl;

	cout << "----------		以下 是 饿汉式	------------" << endl;
	Singleton2 * singer3 = Singleton2::getInstance();
	Singleton2 * singer4 = Singleton2::getInstance();
	if (singer3 == singer4)
		cout << "二者是同一个实例" << endl;
	else
		cout << "二者不是同一个实例" << endl;

	return 0;
}

? ? ? ? 单例对象的创建是通过指针,但是指针没办法自动析构,所以使用了一种类中类的方式进行自动析构,在实例化函数中添加一个类中类的静态变量,在类中类的析构函数中析构单例类对象,因为静态变量的生命周期持续到程序结束,所以在程序结束的时候会自动析构单例对象

class Singelton
{
public:
	static Singleton * getInstance() {
        if (instance == NULL) {
		    static CGarhuishou huishou;
		    instance = new Singelton;
        }
        return instance;
	}
	class CGarhuishou {
	public:
		~CGarhuishou()
		{
			if (Singleton::instance)
			{
				delete Singleton::instance;
				Singleton::instance = NULL;
			}
		}
	};
private:
	Singleton() {}
	static Singleton *instance;
};
Singleton * Singleton::instance = NULL;

3、std::call_once()函数模板

? ? ? ? 第一个参数是标记std::once_flag,第二个参数是函数名,该函数只被调用一次

? ? ? ? 这个类模板具有互斥量的功能

once_flag g_flag;
class Singleton
{
public:
    static void CreateInstance()//call_once保证其只被调用一次
    {
        instance = new Singleton;
    }
    //两个线程同时执行到这里,其中一个线程要等另外一个线程执行完毕
	static Singleton * getInstance() {
         call_once(g_flag, CreateInstance);
         return instance;
	}
private:
	Singleton() {}
	static Singleton *instance;
};
Singleton * Singleton::instance = NULL;

二、条件变量std::condition_variable

? ? ? ? 配合unique_lock使用,当满足某些条件时做出反应

std::mutex mymutex1;
std::unique_lock<std::mutex> sbguard1(mymutex1);
std::condition_variable condition;
condition.wait(sbguard1, [this] {if (!msgRecvQueue.empty())
                                    return true;
                                return false;
                                });
 
condition.wait(sbguard1);

1、wait()与notify_one()

? ? ? ? wait()第二个参数默认false

? ? ? ? wait()第二个参数为false时,解锁互斥量(之前在unique_lock中已经上锁),并阻塞到本行,直到另一个线程中调用了notify_one(),此时wait()被重新唤醒,然后重新判断第二个参数

? ? ? ? wait()第二个参数为true时,继续向下执行

? ? ? ? 如果有多个线程进入wait,notify_one一次会随机唤醒其中某一个线程,如果需要唤醒所有线程则需要使用notify_all()

#include <thread>
#include <iostream>
#include <list>
#include <mutex>
using namespace std;
 
class A {
public:
    void inMsgRecvQueue() {
        for (int i = 0; i < 100000; ++i) 
        {
            cout << "inMsgRecvQueue插入一个元素" << i << endl;

            std::unique_lock<std::mutex> sbguard1(mymutex1);
            msgRecvQueue.push_back(i); 
            //尝试把wait()线程唤醒,执行完这行,
            //那么outMsgRecvQueue()里的wait就会被唤醒
            //只有当另外一个线程正在执行wait()时notify_one()才会起效,否则没有作用
            condition.notify_one();
        }
	}
 
	void outMsgRecvQueue() {
        int command = 0;
        while (true) {
            std::unique_lock<std::mutex> sbguard2(mymutex1);
            // wait()用来等一个东西
            // 如果第二个参数的lambda表达式返回值是false,那么wait()将解锁互斥量,并阻塞到本行
            // 阻塞到什么时候为止呢?阻塞到其他某个线程调用notify_one()成员函数为止;
            //当 wait() 被 notify_one() 激活时,会先执行它的 条件判断表达式 是否为 true,
            //如果为true才会继续往下执行
            condition.wait(sbguard2, [this] {
                if (!msgRecvQueue.empty())
                    return true;
                return false;});
            command = msgRecvQueue.front();
            msgRecvQueue.pop_front();
            //因为unique_lock的灵活性,我们可以随时unlock,以免锁住太长时间
            sbguard2.unlock(); 
            cout << "outMsgRecvQueue()执行,取出第一个元素" << endl;
        }
	}
 
private:
	std::list<int> msgRecvQueue;
	std::mutex mymutex1;
	std::condition_variable condition;
};
 
int main() {
	A myobja;
	std::thread myoutobj(&A::outMsgRecvQueue, &myobja);
	std::thread myinobj(&A::inMsgRecvQueue, &myobja);
	myinobj.join();
	myoutobj.join();
}

? ? ? ? 以上代码中inMsgRecvQueue和outMsgRecvQueue并不是对称执行的

三、async、future、packaged_task、promise

? ? ? ? 需包含新的头文件

#include <future>

1、std::async和std::future创建后台任务并获得返回值

? ? ? ? std::async是一个类模板,用来创建一个异步任务,异步任务的创建方式和线程的创建方式相同

? ? ? ? std::future是一个对象,用来获取线程运行结束后的返回值

? ? ? ? std::future对象有wait()函数,用于等待线程执行结束,效果和join()很像

? ? ? ? std::future对象通过get()函数来获取线程返回值,只能使用一次get,移动语义

#include <iostream>
#include <future>
using namespace std;
class A {
public:
	int mythread(int mypar) {
		cout << mypar << endl;
		return mypar;
	}
};
 
 
int mythread() {
	cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
	std::chrono::milliseconds dura(5000);
	std::this_thread::sleep_for(dura);
	cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
	return 5;
}
 
 
int main() {
	A a;
	int tmp = 12;
	cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
	std::future<int> result1 = std::async(mythread);
	cout << "continue........" << endl;
	cout << result1.get() << endl; //卡在这里等待mythread()执行完毕,拿到结果
	
	//类成员函数
	std::future<int> result2 = std::async(&A::mythread, &a, tmp); //第二个参数是对象引用才能保证线程里执行的是同一个对象
	cout << result2.get() << endl;
   //或者result2.wait();
	cout << "good luck" << endl;
	return 0;
}

? ? ? ?std::future的成员函数std::future_status status = result.wait_for(std::chrono::seconds(几秒));?等待一段时间去判断当前异步任务的状态,std::future_status是枚举类型,有三种状态

????????????????std::future_status::timeout? ?线程还在执行

????????????????std::future_status::ready? 线程执行结束

????????????????std::future_status::deferred? 线程还没开始执行

#include <iostream>
#include <future>
using namespace std;
 
int mythread() {
	cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
	std::chrono::milliseconds dura(5000);
	std::this_thread::sleep_for(dura);
	cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
	return 5;
}

int main() {
	cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
	std::future<int> result = std::async(mythread);
	cout << "continue........" << endl;
	//cout << result1.get() << endl; //卡在这里等待mythread()执行完毕,拿到结果
	//等待1秒
    std::future_status status = result.wait_for(std::chrono::seconds(1));
	if (status == std::future_status::timeout) {
		//超时:表示线程还没有执行完
		cout << "超时了,线程还没有执行完" << endl;
	}
	//类成员函数
	return 0;
}

????????std::async的启动方式std::lunch::deferred表示线程的入口函数延迟执行,直到wait()或者get()开始执行,如果没有wait和get则直接不执行;此方式创建了异步任务但是并没有创建新线程

#include <iostream>
#include <future>
using namespace std;
 
int mythread() {
	cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
	std::chrono::milliseconds dura(5000);
	std::this_thread::sleep_for(dura);
	cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
	return 5;
}
 
 
int main() {
	cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
	std::future<int> result1 = std::async(std::launch::deferred ,mythread);
	cout << "continue........" << endl;
	cout << result1.get() << endl; //卡在这里等待mythread()执行完毕,拿到结果
	cout << "good luck" << endl;
	return 0;
}

????????std::async的启动方式std::lunch::async,在创建异步任务的时候会创建新线程

???????? std::async的启动方式缺省时其参数为std::lunch::async |?std::lunch::deferred? ;并不能确定是立即执行还是延时执行,系统根据当前资源自行决定;可以使用std::future_status进行判断

2、std::packaged_task 打包任务

? ? ? ? 将各种可调用对象封装起来,方便作为线程的入口函数来调用,以及后续获取线程返回值

#include <thread>
#include <iostream>
#include <future>
using namespace std;
 
int mythread(int mypar) {
	cout << mypar << endl;
	cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
	std::chrono::milliseconds dura(5000);
	std::this_thread::sleep_for(dura);
	cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
	return 5;
}
 
int main() {
	cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
	//我们把函数mythread通过packaged_task包装起来
    //参数是一个int,返回值类型是int
    std::packaged_task<int(int)> mypt(mythread);
	std::thread t1(std::ref(mypt), 1);
	t1.join();
	std::future<int> result = mypt.get_future(); 
	//std::future对象里包含有线程入口函数的返回结果,这里result保存mythread返回的结果。
	cout << result.get() << endl;
   
	return 0;
}

? ? ? ? 封装后的对象可以调用get_future()成员函数来得到一个future对象,再使用future的get()函数获取线程的返回值

3、std::promise类模板

? ? ? ? 可以在线程通过set_value()成员函数进行赋值,然后在其他线程中将其取出来

#include <thread>
#include <iostream>
#include <future>
using namespace std;
 
void mythread(std::promise<int> &tmp, int clac) {
	cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
	std::chrono::milliseconds dura(5000);
	std::this_thread::sleep_for(dura);
	cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
	int result = clac;
	tmp.set_value(result); //结果保存到了tmp这个对象中
	return;
}
 
vector<std::packaged_task<int(int)>> task_vec;
 
int main() {
	std::promise<int> myprom;
	std::thread t1(mythread, std::ref(myprom), 180);
	t1.join(); //在这里线程已经执行完了
	std::future<int> fu1 = myprom.get_future(); //promise和future绑定,用于获取线程返回值
	auto result = fu1.get();
	cout << "result = " << result << endl;
}

四、std::shared_future类模板

???????std::shared_future,其get()成员函数复制数据,复制语义

? ? ? ?std::future,其get()成员函数移动数据,移动语义

? ? ? ? future对象的share()函数可以获得shared_future对象,shared_future对象可使用get_future()函数进一步进行复制

#include <thread>
#include <iostream>
#include <future>
using namespace std;
 
int mythread() {
	cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
	std::chrono::milliseconds dura(5000);
	std::this_thread::sleep_for(dura);
	cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
	return 5;
}

int main() {
	cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
	std::packaged_task<int()> mypt(mythread);
	std::thread t1(std::ref(mypt));
	std::future<int> result = mypt.get_future();
	
	bool ifcanget = result.valid(); //判断future中的值是不是一个有效值
	std::shared_future<int> result_s(result.share()); //执行完毕后result_s里有值,而result里空了
	//std::shared_future<int> result_s(std::move(result));
    //通过get_future返回值直接构造一个shared_future对象
    //std::shared_future<int> result_s(mypt.get_future());
    t1.join();
	
	auto myresult1 = result_s.get();
	auto myresult2 = result_s.get();
 
	cout << "good luck" << endl;
	return 0;
}

五、std::atomic原子操作

原子锁用于对一个简单变量的读写操作的保护,效率比mutex高

需要包含头文件

#include <atomic>
#include <iostream>
#include <thread>
#include <atomic>
using namespace std;
std::atomic<int> g_count = 0; //封装了一个类型为int的 对象(值)

void mythread1() {
	for (int i = 0; i < 1000000; i++) {
		g_count++;
	}
}
 
int main() {
	std::thread t1(mythread1);
	std::thread t2(mythread1);
	t1.join();
	t2.join();
	cout << "正常情况下结果应该是200 0000次,实际是" << g_count << endl;
}

????????原子锁适用于** -- += -=等运算符

? ? ? ? 原子变量a执行 a = a + 1;会出错

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-23 10:41:58  更:2022-04-23 10:42:27 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 21:05:44-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码