一、C++线程库的基本使用
进程:运行中的程序,线程:进程中的进程(进程中的一个实例);
1.1、创建,使用
void printHello() {
std::cout << "Hello from thread!\n";
cout << "函数执行结束" << endl;
}
int main()
{
// 创建一个线程,执行 printHello 函数
thread t(printHello);
//t.join(); // 等待线程完成(检查t线程有没有结束)
//分离线程
//t.detach(); // 分离线程,允许线程在后台运行,不需要等待它完成
//joinable() 检查线程是否可以 join--->阻塞
if (t.joinable()) {
t.join(); // 等待线程完成
}
std::cout << "Hello World!\n";
}
二、线程函数中的数据未定义错误
2.1、传递临时变量的问题
void foo(int& x)
{
x += 1;
}
int main()
{
int a = 1;
thread t1(foo,a); // 创建一个线程,传递引用参数=》1是临时变量,而临时值在表达式结束之后就会被销毁,这就会让线程访问到无效的引用。
t1.join(); // 等待线程完成
return 0;
}
void foo(int& x)
{
x += 1;
}
int main()
{
int a = 1;
thread t1(foo,ref(a)); // 创建一个线程,传递引用参数=》1是临时变量,而临时值在表达式结束之后就会被销毁,这就会让线程访问到无效的引用。
t1.join(); // 等待线程完成
return 0;
}
2.2、传递指针或引用指向局部变量的问题
void foo(int& x)
{
x += 1;
}
void test()
{
int a = 11;
t = thread(foo, ref(a)); // 创建一个线程,传递引用参数
}
int main()
{
test(); // 调用 test 函数,创建线程
t.join(); // 等待线程完成 线程对象的生命周期与函数作用域有关
return 0;
}
//修改
void foo(int& x)
{
x += 1;
}
int a =11;
void test()
{
//int a = 11;
t = thread(foo, ref(a)); // 创建一个线程,传递引用参数
}
int main()
{
test(); // 调用 test 函数,创建线程
t.join(); // 等待线程完成 线程对象的生命周期与函数作用域有关
return 0;
}
#include <iostream>
#include <thread>
void foo(int* ptr) {
std::cout << *ptr << std::endl; // 访问已经被销毁的指针
}
int main() {
int x = 1;
std::thread t(foo, &x); // 传递指向局部变量的指针
t.join();
return 0;
}
//在这个例子中,我们定义了一个名为`foo`的函数,它接受一个整型指针作为参数,并输出该指针所指向的整数值。然后,我们创建了一个名为`t`的线程,将`foo`函数以及指向局部变量`x`的指针作为参数传递给它。这样会导致在线程函数执行时,指向局部变量`x`的指针已经被销毁,从而导致未定义行为。
//修改
void foo(int* ptr) {
std::cout << *ptr << std::endl;
delete ptr; // 在使用完指针后,需要手动释放内存
}
int main() {
int* ptr = new int(1); // 在堆上分配一个整数变量
std::thread t(foo, ptr); // 将指针传递给线程
t.join();
return 0;
}
2.3、传递指针或引用指向已经释放的内存的问题
#include <iostream>
#include <thread>
void foo(int* ptr) {
std::cout << *ptr << std::endl; // 访问已经被销毁的指针
}
int main() {
int *ptr = new int(1);
std::thread t(foo, ptr);
delete ptr;//出现野指针
return 0;
}
2.4、类成员函数作为入口函数,类对象被提前释放
#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;
}//或者在t(&MyClass::func, &obj);之后添加t.join()分离线程
class A {
public:
void foo()
{
cout << "A::foo() called" << endl;
}
};
int main()
{
shared_ptr<A> a = make_shared<A>(); // 使用智能指针创建 A 类的实例
thread t1(&A::foo, a); // 创建一个线程,调用 A 类的成员函数
t1.join(); // 等待线程完成
return 0;
}
2.5、入口函数为类的私有成员函数
class MyClass {
private:
friend void myThreadFunc(MyClass* obj);
void privateFunc(){
std::cout << "Thread "
<< std::this_thread::get_id() << " privateFunc" << std::endl;
}
};
void myThreadFunc(MyClass* obj) {
obj->privateFunc();
}
int main() {
MyClass obj;
std::thread thread_1(myThreadFunc, &obj);
thread_1.join();
return 0;
}//使用友元
class A {
private:
friend void thread_foo(); // 声明友元函数
void foo()
{
cout << "A::foo() called" << endl;
}
};
void thread_foo()
{
shared_ptr<A> a = make_shared<A>(); // 使用智能指针创建 A 类的实例
thread t1(&A::foo, a); // 创建一个线程,调用 A 类的成员函数
t1.join(); // 等待线程完成
}
三、互斥量解决多线程数据共享问题
// 互斥量解决多线程数据共享问题.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include <iostream>
#include <string>
#include <memory>
#include <thread>
#include <mutex>
using namespace std;
mutex mtx;
int add(int &a)
{
mtx.lock();
a += 1;
mtx.unlock();
return a;
}
int main()
{
int a = 0;
thread t1(&add, std::ref(a));
thread t2(&add, std::ref(a));
t1.join();
t2.join();
return 0;
}
四、互斥量死锁
void func_1()
{
m1.lock();
m2.lock();
m1.unlock();
m2.unlock();
}
void func_2()
{
m2.lock();
m1.lock();
m1.unlock();
m2.unlock();
}
五、lock_guard与unique_lock
5.1、lock_guard
std::lock_guard 是 C++ 标准库中的一种互斥量封装类,用于保护共享数据,防止多个线程同时访问同一资源而导致的数据竞争问题。
std::lock_guard 的特点如下:
-
当构造函数被调用时,该互斥量会被自动锁定。
-
当析构函数被调用时,该互斥量会被自动解锁。
-
std::lock_guard对象不能复制或移动,因此它只能在局部作用域中使用。 -
//void func1() //{ // for (int i = 0; i < 1000000; i++) // { // lock_guard<mutex> lg(mtx); // shared_data++; // } //}
5.2、std::unique_lock
std::unique_lock 是 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():对互斥量进行解锁操作。
除了上述成员函数外,std::unique_lock 还提供了以下几个构造函数:
-
unique_lock() noexcept = default:默认构造函数,创建一个未关联任何互斥量的std::unique_lock对象。 -
explicit unique_lock(mutex_type& m):构造函数,使用给定的互斥量m进行初始化,并对该互斥量进行加锁操作。 -
unique_lock(mutex_type& m, defer_lock_t) noexcept:构造函数,使用给定的互斥量m进行初始化,但不对该互斥量进行加锁操作。 -
unique_lock(mutex_type& m, try_to_lock_t) noexcept:构造函数,使用给定的互斥量m进行初始化,并尝试对该互斥量进行加锁操作。如果加锁失败,则创建的std::unique_lock对象不与任何互斥量关联。 -
unique_lock(mutex_type& m, adopt_lock_t) noexcept:构造函数,使用给定的互斥量m进行初始化,并假设该互斥量已经被当前线程成功加锁。
void func1()
{
for (int i = 0; i < 1000000; i++)
{
unique_lock<mutex> lg(mtx, defer_lock);//延迟锁定{构造但是不加锁}
lg.lock(); // 显式加锁
shared_data++;
lg.unlock(); // 显式解锁
}
}
六、call_once与其使用场景
单例设计模式是一种常见的设计模式,用于确保某个类只能创建一个实例。由于单例实例是全局唯一的,因此在多线程环境中使用单例模式时,需要考虑线程安全的问题。
(实现有问题)
class Log;
static Log* log = nullptr;
once_flag onceFlag; // 用于确保初始化只执行一次
class Log {
public:
Log() {}
Log(const Log& log) = delete; // 禁止拷贝构造
Log& operator=(const Log& log) = delete; // 禁止拷贝赋值
static Log& GetInstance() {
call_once(onceFlag, init);
return *log; // 返回单例对象
}
static void init() { // 初始化单例对象
if(!log) log = new Log;
}
void PrintLog(string message) {
cout << __TIME__ << ":" << message << endl;
}
};
void prinrError() {
Log::GetInstance().PrintLog("error");
}
int main() {
thread t1(prinrError);
thread t2(prinrError);
t1.join();
t2.join(); // t1 和 t2 都会调用 prinrError 函数,但单例对象只会被创建一次
}
call_once解决多线程中单例模式,对象同时创建的问题。
七、condition_variable与其使用场景
生产者-消费者模型
std::condition_variable 的步骤如下:
-
创建一个
std::condition_variable对象。 -
创建一个互斥锁
std::mutex对象,用来保护共享资源的访问。 -
在需要等待条件变量的地方
使用
std::unique_lock<std::mutex>对象锁定互斥锁并调用
std::condition_variable::wait()、std::condition_variable::wait_for()或std::condition_variable::wait_until()函数等待条件变量。 -
在其他线程中需要通知等待的线程时,调用
std::condition_variable::notify_one()或std::condition_variable::notify_all()函数通知等待的线程。
// condition_variable与其使用场景.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <thread>
#include <condition_variable>
#include <mutex>
#include <queue>
#include <string>
using namespace std;
queue<int> g_queue;
condition_variable g_cv;
mutex mtx;
void Producer()
{
for (int i = 0; i < 10; ++i)
{
unique_lock<mutex> lock(mtx);
//如果队列满了,则等待
g_cv.notify_one();
g_queue.push(i);
cout << "Producer:" << i << endl;
}
this_thread::sleep_for(chrono::microseconds(100));
}
void Consumer()
{
while (1)
{
unique_lock<mutex> lock(mtx);
bool isEmpty = g_queue.empty();
//如果为空,则等待
g_cv.wait(lock, []() {return !g_queue.empty();});
int value = g_queue.front();
g_queue.pop();
cout << "Consumer:" << value << endl;
}
}
int main()
{
thread t1(Producer);
thread t2(Consumer);
t1.join();
t2.join();
}
八、C++实现跨平台线程池
重新学习
#include <iostream>
#include <thread>
#include <queue>
#include <vector>
#include <functional>
#include <condition_variable>
#include <atomic>
#include <mutex>
#include <chrono>
using namespace std;
// 跨平台线程池实现
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); // 使用unique_lock代替lock_guard以便于手动解锁
condition.wait(lock, [this]() { return !tasks.empty() || stop; }); // 等待任务或停止信号
if (stop && tasks.empty()) return; // 如果停止且任务队列为空,则退出线程
function<void()> task = move(tasks.front()); // 获取任务
tasks.pop(); // 移除任务
//lock.unlock(); // 手动解锁,允许其他线程访问任务队列
task(); // 执行任务
}
});
}
}
template<class F, class... Args>
void enqueue(F&& f, Args&&... args)//&&万能引用,允许传递任意类型的可调用对象和参数
{
function<void()> task = bind(forward<F>(f), forward<Args>(args)...); // 绑定任务 C++111完美转发,允许传递任意类型的可调用对象和参数
{
unique_lock<mutex> lock(mtx); // 使用unique_lock代替lock_guard以便于手动解锁
tasks.emplace(move(task)); // 将任务添加到队列
}
condition.notify_one(); // 通知一个等待线程
}
~ThreadPool()
{
{
unique_lock<mutex> lock(mtx);
stop = true; // 设置停止标志
}
condition.notify_all(); // 通知所有线程
for (auto &t : threads) {
t.join(); // 等待线程结束
}
}
private:
vector<thread> threads; // 修复:定义线程数组
queue<function<void()>> tasks; // 任务队列
mutex mtx;
condition_variable condition;
bool stop;
};
int main()
{
ThreadPool pool(4); // 创建一个包含4个线程的线程池
// 向线程池添加任务
for (int i = 0; i < 10; ++i) {
pool.enqueue([i]() {
cout << "Task " << i << " is running;" << endl;
this_thread::sleep_for(chrono::seconds(1)); // 模拟任务执行时间
cout << "Task " << i << " completed." << endl;
});
}
return 0;
}
九、异步并发
1、async
// 异步并发.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 //
#include <iostream>
#include <future>
using namespace std;
int func() {
int i = 0;
for (int i = 0;i < 100;i++)
{
i++;
}
return i;
}
void func1(promise<int> f)
{
f.set_value(1000); // 设置 promise 的值
}
int main()
{
/*packaged_task<int()> task(func); // 创建一个打包任务
auto featured_result = task.get_future(); // 获取未来对象
std::thread t1(std::move(task)); // 将打包任务移动到线程中执行
cout << featured_result.get() << endl; // 获取异步函数的返回值
future<int> future_result = async(launch::async, func);//不阻塞继续往下运行
// 这里可以执行其他操作
cout << func() << endl;
cout << future_result.get() << endl; // 获取异步函数的返回值*/
//异步编程
promise<int> f;// 创建一个 promise 对象
auto future_result = f.get_future(); // 获取与 promise 关联的 future 对象
std::thread t1(func1, move(f)); // 将 promise 对象传递给线程
t1.join(); // 等待线程完成
cout << future_result.get() << endl; // 获取异步函数的返回值
return 0;
}
2、future
十、原子操作atomic
#include <iostream>
#include <future>
using namespace std;
int func() {
int i = 0;
for (int i = 0;i < 100;i++)
{
i++;
}
return i;
}
void func1(promise<int> f)
{
f.set_value(1000); // 设置 promise 的值
}
int main()
{
/*packaged_task<int()> task(func); // 创建一个打包任务
auto featured_result = task.get_future(); // 获取未来对象
std::thread t1(std::move(task)); // 将打包任务移动到线程中执行
cout << featured_result.get() << endl; // 获取异步函数的返回值
future<int> future_result = async(launch::async, func);//不阻塞继续往下运行
// 这里可以执行其他操作
cout << func() << endl;
cout << future_result.get() << endl; // 获取异步函数的返回值*/
//异步编程
promise<int> f;// 创建一个 promise 对象
auto future_result = f.get_future(); // 获取与 promise 关联的 future 对象
std::thread t1(func1, move(f)); // 将 promise 对象传递给线程
t1.join(); // 等待线程完成
cout << future_result.get() << endl; // 获取异步函数的返回值
return 0;
}
2034

被折叠的 条评论
为什么被折叠?



