C++并发编程

news/2024/9/23 10:20:42/

基本介绍

线程

  • C++98标准没有直接提供原生的多线程支持

  • 在C++98中,并没有像后来的C++11标准中那样的<thread>库或其他直接的多线程工具

  • 然而,这并不意味着在C++98中无法实现多线程。开发者通常会使用平台特定的API(如Windows的线程API或POSIX线程(pthreads))来实现多线程。此外,一些第三方库,如Boost.Thread,也提供了在C++98环境中进行多线程编程的功能

  • 需要注意的是,使用平台特定的API或第三方库进行多线程编程可能会增加代码的复杂性和维护成本,因为你需要确保你的代码在所有目标平台上都能正常工作,并处理各种可能的线程同步和互斥问题

  • 从C++11开始,C++标准库开始直接支持多线程编程,通过引入<thread><mutex><condition_variable>等头文件,提供了更为方便和统一的多线程编程接口。因此,如果可能的话,建议使用C++11或更高版本的C++标准进行多线程编程

进程

  • C++标准库本身并没有直接提供创建进程的功能,创建进程通常依赖于操作系统的API或其他库函数

  • 在Unix和Linux系统中,可以使用fork()函数来创建进程。fork()函数会创建一个与当前进程几乎完全相同的子进程,包括代码、数据和堆栈等。在子进程中,可以使用exec()系列函数来执行另一个程序

  • 在Windows系统中,可以使用CreateProcess()函数来创建进程。这个函数会创建一个新的进程,并返回一个进程句柄,可以用于操作该进程

创建线程

#include<iostream>
#include<string>
#include<thread>void printHelloWorld()
{std::cout << "Hello World" << std::endl;
}void print(std::string text)
{std::cout << text << std::endl;
}int main()
{// 当前 main 这里是主线程// 创建一个线程 t1,让它执行 printHelloWorld 这个函数std::thread t1(printHelloWorld);// 等待 t1 线程完成(如果不等待,可能子线程 t1 还没完成的时候,主线程已经结束了,程序会报错)t1.join();// 创建一个线程 t2,让它执行 print 这个函数,并传入参数std::thread t2(print, "This is thread 2.");// 等待 t2 线程完成(如果不等待,可能子线程 t2 还没完成的时候,主线程已经结束了,程序会报错)t2.join();// 创建一个线程 t3std::thread t3(print, "This is thread 3.");// 分离线程(也可以使用分离线程这个技术,让主线程结束后,子线程依然可以运行)t3.detach();// 创建一个线程 t4std::thread t3(print, "This is thread 3.");// 严谨的项目里面,可能用到,先判断该线程是否可以被join()bool isJoin = t3.joinable();if (isJoin){t3.join();}return 0;
}

线程常见错误

  1. 传递临时变量的问题
#include<iostream>
#include<thread>void foo(int& x)
{x += 1;
}int main()
{int num = 1;		// 局部变量 numstd::thread t1(foo, std::ref(num));		// std::ref() 传递引用类型t1.join();		// 等待t1线程结束std::cout << num << std::endl;return 0;
}
  1. 传递指针或引用指向局部变量的问题
#include<iostream>
#include<thread>// 创建一个线程 t (全局变量)
std::thread t;
int a = 1;void foo(int& x)
{std::cout << x << std::endl;		// 1x += 1;std::cout << x << std::endl;		// 2
}void test()
{t = std::thread(foo, std::ref(a));
}int main()
{test();t.join();return 0;
}
  1. 入口函数为类的私有成员函数
#include<iostream>
#include<thread>
#include<memory>	// 智能指针,不用的时候,会自动释放class A
{
private:friend void thread_foo();void foo(){std::cout << "hello" << std::endl;}
};void thread_foo()
{std::shared_ptr<A> a = std::make_shared<A>();	// 使用智能指针实例化A对象std::thread t(&A::foo, a);t.join();
}int main()
{thread_foo();
}

互斥量

锁的使用

#include<iostream>
#include<thread>
#include<mutex>int a = 0;// 创建互斥锁
std::mutex mtx;void func()
{for (int i = 0; i < 10000; i++){mtx.lock();		// 加锁a += 1;mtx.unlock();	// 解锁}
}int main()
{std::thread t1(func);std::thread t2(func);t1.join();t2.join();std::cout << a << std::endl;return 0;
}

死锁演示

  • 图形演示

在这里插入图片描述

  • 代码演示
#include<iostream>
#include<thread>
#include<mutex>// 创建互斥锁
std::mutex mtx1;
std::mutex mtx2;// people1 先抢占 mtx1,再快速抢占 mtx2
void people1()
{for (int i = 0; i < 1000; i++){mtx1.lock();std::cout << "people1 上锁mtx1成功\n";mtx2.lock();std::cout << "people1 上锁mtx2成功\n";mtx1.unlock();mtx2.unlock();}
}// people2 先抢占 mtx2,再快速抢占 mtx1
void people2()
{for (int i = 0; i < 1000; i++){mtx2.lock();std::cout << "people2 上锁mtx2成功\n";mtx1.lock();std::cout << "people2 上锁mtx1成功\n";mtx1.unlock();mtx2.unlock();}
}int main()
{std::thread t1(people1);std::thread t2(people2);t1.join();t2.join();return 0;
}

解决死锁

  • 解决办法:所有人都要严格按照顺序抢占资源,都要先抢占完A资源,才能继续抢占B资源,继续抢占C资源…
#include<iostream>
#include<thread>
#include<mutex>// 创建互斥锁
std::mutex mtx1;
std::mutex mtx2;// people1 要先抢占 mtx1,才能抢占 mtx2
void people1()
{for (int i = 0; i < 1000; i++){mtx1.lock();std::cout << "people1 上锁mtx1成功\n";mtx2.lock();std::cout << "people1 上锁mtx2成功\n";mtx1.unlock();std::cout << "people1 解锁mtx1成功\n";mtx2.unlock();std::cout << "people1 解锁mtx2成功\n";}
}// people2 要先抢占 mtx1,才能抢占 mtx2
void people2()
{for (int i = 0; i < 1000; i++){mtx1.lock();std::cout << "people2 上锁mtx1成功\n";mtx2.lock();std::cout << "people2 上锁mtx2成功\n";mtx1.unlock();std::cout << "people2 解锁mtx1成功\n";mtx2.unlock();std::cout << "people2 解锁mtx2成功\n";}
}int main()
{std::thread t1(people1);std::thread t2(people2);t1.join();t2.join();return 0;
}

lock_guard

  • std::lock_guard 是 C++ 标准库中的一种互斥量封装类,用于保护共享数据,防止多个线程同时访问同一资源而导致的数据竞争问题
  • 当构造函数被调用时,该互斥量会被自动锁定
  • 当析构函数被调用时,该互斥量会被自动解锁
  • std::lock_guard 对象不能复制或移动,因此它只能在局部作用域中使用
#include<iostream>
#include<thread>
#include<mutex>// 共享变量
int shared_data = 0;// 创建互斥锁
std::mutex mtx;void func()
{for (int i = 0; i < 1000; i++){std::lock_guard<std::mutex> obj(mtx);	// 构造时自动加锁,析构时自动解锁shared_data++;}
}int main()
{std::thread t1(func);std::thread t2(func);t1.join();t2.join();std::cout << shared_data << std::endl;		// 2000return 0;
}

unique_lock

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

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

lock():尝试对互斥量进行加锁操作,如果当前互斥量已经被其他线程持有,则当前线程会被阻塞,直到互斥量被成功加锁。try_lock():尝试对互斥量进行加锁操作,如果当前互斥量已经被其他线程持有,则函数立即返回 false,否则返回 truetry_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 进行初始化,并假设该互斥量已经被当前线程成功加锁

文章推荐

  • std::thread 快速上手:https://blog.csdn.net/m0_75215937/article/details/135007590
  • std::thread 更多内容:https://blog.csdn.net/sjc_0910/article/details/118861539
  • c++ 多线程编程:https://zhuanlan.zhihu.com/p/547312117

http://www.ppmy.cn/news/1436128.html

相关文章

统一SQL 支持Oracle decode函数到TDSQL-MySQL的转换

统一SQL介绍 https://www.light-pg.com/docs/LTSQL/current/index.html 源和目标 源数据库&#xff1a;Oracle 目标数据库&#xff1a;TDSQL-MySQL 操作目标 在Oracle中&#xff0c;decode函数语法如下图&#xff1a;该函数功能是将 expr与每个 search 依次做比较&#x…

探索比特币符文热:市场趋势与持续性分析

在加密货币世界中&#xff0c;比特币一直是备受关注的焦点之一。然而&#xff0c;近年来&#xff0c;随着DeFi&#xff08;去中心化金融&#xff09;的兴起&#xff0c;一种新的潮流开始崭露头角——比特币符文。本文将探讨比特币符文的兴起&#xff0c;分析市场趋势&#xff0…

qt——窗口置灰不可操作

在Qt中实现一个窗口&#xff08;或窗口中的特定部分&#xff09;置灰并不可操作&#xff0c;通常涉及到两个概念&#xff1a;禁用窗口的交互功能以及视觉上的置灰效果。下面我会介绍如何使用Qt实现这两个功能。 1. 禁用窗口的交互功能 如果你希望整个窗口都不可交互&#xff0c…

卷积神经网络CNN手写数字识别案例

目录 网络设计 设计两个卷积大层 具体参数 代码&#xff1a; 结果&#xff1a; 网络设计 设计两个卷积大层 手写数字识别数据集下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1nqhP4yPNcqefKYs91jp9ng?pwdxe1h 提取码&#xff1a;xe1h 我们知道mnist图像…

Facebook账号运营要用什么IP?

众所周知&#xff0c;Facebook封号大多数情况都是因为IP的原因。Facebook对于用户账号有严格的IP要求和限制&#xff0c;以维护平台的稳定性和安全性。在这种背景下&#xff0c;海外IP代理成为了一种有效的解决方案&#xff0c;帮助用户避免检测&#xff0c;更加快捷安全地进行…

Python 基础 (Pandas):Pandas 入门

1. 官方文档 API reference — pandas 2.2.2 documentation 2. 准备知识&#xff1a;Pandas 数据结构 Series & DataFrame 2.1 Series 2.1.1 创建 Series 类型数据 一个 Series 对象包含两部分&#xff1a;值序列、标识符序列。可通过 .values (返回 NumPy ndarry 类型…

react入门基础

React 简介 React ------- 用于构建用户界面的JavaScript库/一个将数据渲染为HTML视图的开源JavaScript库 React历史 【经典水时长/水字数】 由Facebook开发&#xff0c;且开源 起初由Facebook的软件工程师 Jordan Walke 创建 【膜拜 React 祖师爷】于2011年部署于 Facebo…

Java面向对象各个知识点的总结

Java面向对象各个知识点的总结 文章目录 Java面向对象各个知识点的总结前言补充一、面向对象编程概述二、类的成员1—成员变量三、类的成员2—方法5.1 方法介绍&理解5.2 方法的重载(overload)5.3 可变个数形参的方法5.4 方法的参数传递机制&#xff1a;值传递(重点、难点)5…