C++11多线程2

C++11多线程2,第1张

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

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

1、单例设计模式

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

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

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

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

#include 	
#include 
using namespace	std;

mutex myMutex;
//懒汉模式
class Singleton
{
public:
	static Singleton * getInstance() {
         //双重锁定 提高效率
		if (instance == NULL) {
			lock_guard 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 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 
#include 
#include 
#include 
using namespace std;
 
class A {
public:
    void inMsgRecvQueue() {
        for (int i = 0; i < 100000; ++i) 
        {
            cout << "inMsgRecvQueue插入一个元素" << i << endl;

            std::unique_lock 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 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 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 

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

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

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

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

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

#include 
#include 
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 result1 = std::async(mythread);
	cout << "continue........" << endl;
	cout << result1.get() << endl; //卡在这里等待mythread()执行完毕,拿到结果
	
	//类成员函数
	std::future 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 
#include 
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 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 
#include 
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 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 
#include 
#include 
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 mypt(mythread);
	std::thread t1(std::ref(mypt), 1);
	t1.join();
	std::future 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 
#include 
#include 
using namespace std;
 
void mythread(std::promise &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> task_vec;
 
int main() {
	std::promise myprom;
	std::thread t1(mythread, std::ref(myprom), 180);
	t1.join(); //在这里线程已经执行完了
	std::future 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 
#include 
#include 
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 mypt(mythread);
	std::thread t1(std::ref(mypt));
	std::future result = mypt.get_future();
	
	bool ifcanget = result.valid(); //判断future中的值是不是一个有效值
	std::shared_future result_s(result.share()); //执行完毕后result_s里有值,而result里空了
	//std::shared_future result_s(std::move(result));
    //通过get_future返回值直接构造一个shared_future对象
    //std::shared_future 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 
#include 
#include 
#include 
using namespace std;
std::atomic 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;会出错

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/716917.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-25
下一篇 2022-04-25

发表评论

登录后才能评论

评论列表(0条)

保存