C++多线程学习

本文详细介绍了C++中线程的基本概念、创建、同步机制(如join,detach,mutex,atomic等)、线程函数参数传递、线程池、异步编程(async,packaged_task,promise)以及单例模式在多线程中的应用。
摘要由CSDN通过智能技术生成

1.主程序等待线程执行完毕
我们先来写一个最最最基本的线程代码

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

void print()
{
	cout << "Hello World" << endl;
}

int main()
{
	//1.创建线程
	std::thread thread1(print);
	return 0;
}

当我们打断点的时候可以发现,当执行到std::thread thread1(print)时main函数并不会直接跳到print函数里等待其执行完成之后再retun 0,而是直接return,所以这里会报错。
所以这时候我们需要join函数

	std::thread thread1(print);
	thread1.join();

这时候就不会报错了,但是这里使用join会导致主线程一直等待子线程,这时候可以使用detach()

std::thread thread1(print);
	thread1.detach();

这里detach调用之后程序不会执行任何东西,因为主函数已经结束了,但是print也实实在在被调用了
在这里插入图片描述
优化:


int main()
{
	//1.创建线程
	std::thread thread1(print);
	if (thread1.joinable())
	{
		thread1.join();
	}
	return 0;
}

我们接着来写下一段代码

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

void f(int & x)
{
	x += 1;
}

int main()
{
	int a = 1;
	std::thread thread1(f, &a);
	thread1.join();
	return 0;
}

这里会编译失败,原因是thread 构造函数的参数需要是可调用的对象,并且线程函数的参数会被拷贝或移动传递给线程函数。然而,整数 a 的地址并不是可调用的对象,因此无法直接将其作为参数传递给线程函数。所以需要将&a修改成ref(a)

std::thread thread1(f, ref(a));

同理,我们写成指针形式

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

void f(int * x)
{
	*x += 1;
}

int main()
{
	int* a = new int(1);
	std::thread thread1(f, a);
	thread1.join();
	return 0;
}

这样不会报错,但在调用的下面使用delete便会马上报错

int main()
{
	int* a = new int(1);
	std::thread thread1(f, a);
	delete(a);
	thread1.join();
	return 0;
}

在这里插入图片描述
接下来是类版本的

#include <iostream>
#include <thread>
#include<memory>
using namespace std;

class A {
public:
	void f() {
		std::cout << "Hello" << endl;
	}
};

int main()
{
	shared_ptr<A> a = make_shared<A>();
	std::thread t(&A::f, a);
	t.join();
}

这里使用智能指针a来管理内存

接下来我们研究竞争的问题

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

int a = 0;

void f()
{
	for (int i = 0; i < 10000; i++)
		a += 1;
}

int main()
{
	std::thread t1(f);
	std::thread t2(f);
	t1.join();
	t2.join();

	std::cout << a << endl;
	return 0;
}

这里预期应该是20000,但是却是一个小于两万的数,所以这里用一个互斥锁

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

int a = 0;
std::mutex mtx;
void f()
{
	for (int i = 0; i < 10000; i++)
	{
		mtx.lock();
		a += 1;
		mtx.unlock();
	}		
}

int main()
{
	std::thread t1(f);
	std::thread t2(f);
	t1.join();
	t2.join();
	std::cout << a << endl;
	return 0;
}

然后我们学习lock_guard
这个lock_guard有点像智能指针,自己会释放锁

for (int i = 0; i <10000; i++)
	{
		lock_guard<mutex>lg(mtx);
		a += 1;
	}	

接着是unique_guard
unique_guard比lock_guard方法多一些,所以消耗的资源要多一些
主要多加了延迟加锁的操作,这里简单对这个进行操作

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

int a = 0;
std::timed_mutex mtx;
void f()
{
	for (int i = 0; i <2; i++)
	{
		unique_lock<timed_mutex>lg(mtx,defer_lock);
		if (lg.try_lock_for(chrono::seconds(2)))
		{
			std::this_thread::sleep_for(std::chrono::seconds(1));
			a++;
		}
	}		
}

int main()
{
	std::thread t1(f);
	std::thread t2(f);
	t1.join();
	t2.join();
	std::cout << a << endl;
	return 0;
}

然后我们学习一下线程中的单例,首先介绍一下懒汉模式,提前把静态变量声明好,需要直接拿就行

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

class Log
{
public:
	Log() {};
	Log(const Log& log) = delete;
	Log& operator=(const Log& log) = delete;

	static Log& GetInstance() 
	{
		static Log log;
		return log;
	}
	void PrintLog(std::string msg)
	{
		std::cout << msg << endl;
	}
private:
	std::once_flag once;
};

int main()
{
	
}

然后是饿汉模式,只有需要的时候才调用

#include <iostream>
#include <mutex>
#include <string>

static Log* log = nullptr;
class Log
{
public:
    static Log& GetInstance()
    {
        if (!log)
            return *log;
    }

    void PrintLog(std::string msg)
    {
        std::cout << msg << std::endl;
    }

    Log() {}
    Log(const Log& log) = delete;
    Log& operator=(const Log& log) = delete;

private:
    std::once_flag once;
};

int main()
{
    Log& log = Log::GetInstance();
    log.PrintLog("Hello, World!");

    return 0;
}

这里可以用call_once来确保函数只会被执行一份

之后是生产者-消费者模型
这里使用q_cv.notify_one()来通知消费者线程,所以最后的执行结果就是生产者填一个进去,然后消费者再拿一个出来

#include <iostream>
#include <mutex>
#include <string>
#include<condition_variable>
#include<queue>

std::queue<int>q;
std::condition_variable q_cv;
std::mutex mtx;

void Producer() {
	for (int i = 0; i < 10; i++) 
	{
		std::unique_lock<std::mutex>lock(mtx);
		q.push(i);
		q_cv.notify_one();
		//通知消费者取任务
		std::cout << "task " << i << std::endl;
	}
	std::this_thread::sleep_for(std::chrono::microseconds(100));
		
}
void Consumer() 
{
	while (1)
	{
		std::unique_lock<std::mutex>lock(mtx);
		bool isempty = q.empty();
		q_cv.wait(lock, []() { return !q.empty(); });
		int value = q.front();
		q.pop();
		std::cout << "Consumer" << value << std::endl;
	}
}

int main()
{
	std::thread t1(Producer);
	std::thread t2(Consumer);
	t1.join();
	t2.join();
	return 0;
}
#include <iostream>
#include <thread>
#include <mutex>
#include <string>
#include <condition_variable>
#include <queue>
#include <vector>
#include <functional>
#include <chrono> // 添加这个头文件来使用 sleep_for

using namespace std;

class ThreadPool {
public:
    ThreadPool(int numThreads) : stop(false) {//使用成员初始化列表把stop设置为false
        for (int i = 0; i < numThreads; ++i) { //针对线程数
            threads.emplace_back([this] {     //[this] 表示捕获当前对象的指针,使得 lambda 表达式可以访问当前对象的成员变量和成员函数。
                while (true) {
                    unique_lock<mutex> lock(mtx);
                    condition.wait(lock, [this] { //当线程池不为空时占用
                        return !tasks.empty() || stop;
                        });
                    if (stop && tasks.empty())
                        return;

                    function<void()> task(move(tasks.front()));//使用 std::move() 可以将资源的所有权转移,避免额外的拷贝操作。
                    tasks.pop();
                    lock.unlock();
                    task();
                }
                });
        }
    }

    ~ThreadPool() {
        {
            std::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) //用于向线程池中添加任务,F 是任务函数的类型,而 Args... 是任务函数的参数类型。
    {
        function<void()> task = bind(forward<F>(f), args...);
        {
            unique_lock<mutex> lock(mtx);
            tasks.emplace(move(task));
        }
        condition.notify_one();
    }

private:
    vector<thread> threads;
    queue<function<void()>> tasks;//存储任务函数
    condition_variable condition;
    mutex mtx;
    bool stop;
};

int main() {
    ThreadPool pool(4);
    for (int i = 0; i < 10; i++) {
        pool.enqueue([i] {
            cout << "Task " << i << " is running" << endl;
            this_thread::sleep_for(std::chrono::seconds(1));
            cout << "Task " << i << " is done" << endl;
            });
    }
    this_thread::sleep_for(std::chrono::seconds(5)); // 等待所有任务完成
    return 0;
}

接下来讲异步并发
async的运行

#include <iostream>
#include <future>

using namespace std;

int func()
{
	int i = 0;
	for ( i = 0; i < 1000; i++) {
		i++;
	}
	return i;
}
int main()
{
	future<int> future_result = async(launch::async, func);//调用的时候func已经开始运行了,结果存在future_result
	cout << func() << endl;
	cout << future_result.get() << endl;//如果没有运行完,get会等待
	return 0;
}

然后是packaged_task

#include <iostream>
#include <future>

using namespace std;

int func()
{
	int i = 0;
	for ( i = 0; i < 1000; i++) {
		i++;
	}
	return i;
}
int main()
{
	packaged_task<int()>task(func);
	auto future_result = task.get_future();//此时只是把fun包裹,并没有运行
	thread t1(move(task));//这里必须用move 
	cout << func()<<endl;
	t1.join();
	cout << future_result.get() << endl;
	return 0;
}

promise是一个类模板,用于在一个线程产生一个值,并在另一个线程获取这个值

#include <iostream>
#include <future>

using namespace std;

void func(promise<int>&f) {
	f.set_value(1000);
}

int main()
{
	promise<int>f;
	auto future_result = f.get_future();//这个 future 对象可以用来获取 promise 对象设置的值。
	thread t1(func, ref(f));
	t1.join();
	cout << future_result.get() << endl;
	return 0;
}

最后一个atomic
用atomic解决之前互斥的问题
atomic是C++11标准库的一个模板类,用于实现多线程下的原子操作。它提供了一种线程安全的方式来访问和修改共享变量,可以避免多线程环境中的数据竞争问题。

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

int a = 0;
std::mutex mtx;

std::atomic<int>shared_data = 0;
void f()
{
	for (int i = 0; i < 10000; i++)
	{
		shared_data++;
	}
}

int main()
{
	std::thread t1(f);
	std::thread t2(f);
	t1.join();
	t2.join();
	std::cout << a << endl;
	return 0;
}

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值