4.7 C++ Boost 多线程并发库
Boost 库是一个由C/C++语言的开发者创建并更新维护的开源类库,其提供了许多功能强大的程序库和工具,用于开发高质量、可移植、高效的C应用程序。Boost库可以作为标准C库的后备,通常被称为准标准库,是C标准化进程的重要开发引擎之一。使用Boost库可以加速C应用程序的开发过程,提高代码质量和性能,并且可以适用于多种不同的系统平台和编译器。Boost库已被广泛应用于许多不同领域的C++应用程序开发中,如网络应用程序、图像处理、数值计算、多线程应用程序和文件系统处理等。
C++语言并没有对多线程与网络的良好支持,虽然新的C++标准加入了基本的thread库,但是对于并发编程的支持仍然很基础,Boost库提供了数个用于实现高并发与网络相关的开发库这让我们在开发跨平台并发网络应用时能够像Java等语言一样高效开发。
thread库为C++增加了多线程处理能力,其主要提供了清晰的,互斥量,线程,条件变量等,可以很容易的实现多线程应用开发,而且该库是可跨平台的,并且支持POSIX和Windows线程。
7.1 互斥锁
互斥锁通过在访问共享资源的线程之间进行通信来避免并发问题。互斥锁仅允许一个线程在任何给定时间点上访问共享资源。如果已经有一个线程锁定了互斥锁,则任何其他线程都必须等待锁被释放。一旦锁被释放,等待队列中的一个线程将被允许继续其工作。
Boost库中的 boost::mutex 类型表示一个互斥锁。它提供了两个主要函数来控制互斥锁:lock() 和 unlock()。当一个线程想要访问一个共享资源时,它会调用互斥锁的 lock() 函数来获取锁,如果无法获得,线程将最多等待直到锁被释放。在线程访问完共享资源后,它需要调用 unlock() 函数来释放锁,以便其他线程可以获得锁并访问共享资源。
互斥体是用于线程同步的一种手段,其主要用于在多线程环境下,防止多个线程同时操作共享资源,当某线程被锁,其他线程则需要等待它解锁后才能继续访问共享资源。
- thread提供了6种互斥类型,但常用的只有3种:
- mutex 独占互斥锁
- recursive_mutex 递归互斥锁
- shared_mutex 读写锁
通常我们会使用Mutex来保护共享资源,防止在多线程环境中数据的不一致性,当一个资源被锁定,其他线程只能阻塞等待释放后才可继续操作。
#define BOOST_THREAD_VERSION 5
#include <iostream>
#include <boost/thread/thread_guard.hpp>
using namespace std;
using namespace boost;
// 最基本的互斥锁
void MutexA()
{
boost::mutex mutex;
try
{
mutex.lock();
// 执行变量存取操作
mutex.unlock();
}
catch (...)
{
mutex.unlock();
}
}
// 智能互斥锁(无需加解锁)
void MutexB()
{
boost::mutex mutex;
boost::lock_guard<boost::mutex> global_mutex(mutex);
// 只需要定义以上代码即可
}
在Boost中创建多线程非常简单,只需要定义一个MyThread线程函数,并在主函数中开启线程即可实现。
#include <iostream>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/bind.hpp>
using namespace std;
boost::mutex io_mutex;
void MyThread(int id)
{
for (int i = 1; i < 10; ++i)
{
// 定义智能互斥锁,防止出现输出乱序
boost::mutex::scoped_lock lock(io_mutex);
std::cout << id << ": " << i << std::endl;
}
}
int main(int argc, char *argv[])
{
boost::thread thrd1(boost::bind(&MyThread, 1));
boost::thread thrd2(boost::bind(&MyThread, 2));
//中断线程
thrd1.interrupt();
//获取线程ID
cout << "线程ID:" << thrd1.get_id() << endl;
// 等待线程
thrd1.join();
thrd2.join();
//超过3s结束线程
thrd1.timed_join(boost::posix_time::seconds(3));
thrd2.timed_join(boost::posix_time::seconds(3));
std::system("pause");
return 0;
}
7.2 线程局部存储
Boost库中提供了线程局部存储(Thread Local Storage,简称TLS)的支持,可以让程序中的每个线程都拥有独立的数据空间,互相之间不会受到干扰。这对于一些线程之间需要共享数据,但需要保证数据安全的场景非常有用,例如线程池等。
有时候函数使用了局部静态变量或全局变量,导致无法用于多线程环境,因为无法保证变量在多线程环境下重入的正确操作。
#include <iostream>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/tss.hpp>
using namespace std;
// 定义一个全局互斥体
boost::mutex io_mutex;
// 线程本地存储一个整数,声明
boost::thread_specific_ptr<int> ptr;
struct MyThread
{
MyThread(int id) :id(id){}
void operator()()
{
// 如果ptr内部为0则说明没有,我们就初始化为0
if (ptr.get() == 0)
ptr.reset(new int(0));
for (int x = 0; x <10; ++x)
{
// 往自己的线程上加
(*ptr)++;
boost::mutex::scoped_lock lock(io_mutex);
std::cout << "当前ID: " << id << " 本地存储数值: " << *ptr << std::endl;
}
}
public:
int id;
};
int main(int argc, char *argv[])
{
boost::thread thrd1(MyThread(1));
boost::thread thrd2(MyThread(2));
thrd1.join();
thrd2.join();
std::system("pause");
return 0;
}
如果本地存储的类型是一个结构体,如下定义了MyStruct本地结构体,来实现本地数据累加。
#include <iostream>
#include <string>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/tss.hpp>
using namespace std;
// 定义一个全局互斥体
boost::mutex io_mutex;
// 定义本地存储结构体
typedef struct MyStruct
{
int uid;
std::string uname;
MyStruct(int x, std::string y)
{
uid = x;
uname = y;
}
}MyStruct;
// 线程本地存储一个整数,声明
boost::thread_specific_ptr<MyStruct> ptr;
struct MyThread
{
MyThread(int id) :id(id){}
void operator()()
{
// 如果ptr内部为0则说明没有,我们就初始化为0
if (ptr.get() == 0)
ptr.reset(new MyStruct(0,"lyshark"));
for (int x = 0; x <10; ++x)
{
// 往自己的线程上加
(*ptr).uid = (*ptr).uid + 1;
(*ptr).uname = "lyshark";
boost::mutex::scoped_lock lock(io_mutex);
std::cout << "当前ID: " << id << " 本地存储数值: " << (*ptr).uid << "本地存储名字: " << (*ptr).uname << std::endl;
}
}
public:
int id;
};
int main(int argc, char *argv[])
{
boost::thread thrd1(MyThread(1));
boost::thread thrd2(MyThread(2));
thrd1.join();
thrd2.join();
std::system("pause");
return 0;
}
7.3 使用线程组
线程组thread_group用于管理一组线程,就像线程池一样,其内部使用了std::list<thread*>来容纳每个线程对象。
当需要创建新线程时,使用create_thread()工厂函数,并通过bind绑定传递参数即可实现创建,如下是最简单的线程组创建。
#include <iostream>
#include <boost/thread.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
using namespace std;
boost::mutex io_mutex;
void MyThread(int x, string str)
{
try
{
// 延时2秒
boost::this_thread::sleep(boost::posix_time::seconds(2));
for (int i = 0; i < x; i++)
{
boost::mutex::scoped_lock lock(io_mutex);
cout << "输出字符串: " << str << " 计次: " << i << endl;
}
}
catch (boost::thread_interrupted&)
{
cout << "thread is interrupt" << endl;
}
}
int main(int argc,char *argv[])
{
// 定义线程组
boost::thread_group group;
for (int x = 0; x < 10; x++)
{
// 创建新线程
group.create_thread(boost::bind(MyThread, x, "hello lyshark"));
}
cout << "当前线程数量: " << group.size() << endl;
group.join_all();
std::system("pause");
return 0;
}
我们还可以通过add_thread和remove_thread将特定的线程对象放入到不同的线程组中,来实现对线程的批量操作。
#include <iostream>
#include <string>
#include <boost/thread.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
using namespace std;
typedef struct MyStruct
{
int uuid;
std::string uname;
}MyStruct;
boost::mutex io_mutex;
void MyThread(MyStruct ptr)
{
try
{
for (int i = 0; i < 5; i++)
{
boost::mutex::scoped_lock lock(io_mutex);
cout << "UUID: " << ptr.uuid << " UName: " << ptr.uname << endl;
}
}
catch (boost::thread_interrupted&)
{
cout << "thread is interrupt" << endl;
}
}
int main(int argc,char *argv[])
{
MyStruct my_struct;
boost::thread_group group;
// 创建线程并赋值
my_struct.uuid = 1001;
my_struct.uname = "lyshark";
boost::thread thrd1(&MyThread,my_struct);
my_struct.uuid = 1002;
my_struct.uname = "admin";
boost::thread thrd2(&MyThread, my_struct);
// 将线程加入线程组
group.add_thread(&thrd1);
group.add_thread(&thrd2);
// 中断所有线程
// group.interrupt_all();
// 判断thrd1是否在组内
bool is_in = group.is_thread_in(&thrd1);
std::cout << "是否在组内: " << is_in << std::endl;
// 移除线程组
group.remove_thread(&thrd1);
group.remove_thread(&thrd2);
// 等待线程组执行结束
group.join_all();
boost::this_thread::sleep(boost::posix_time::seconds(2));
std::system("pause");
return 0;
}
7.4 获取线程返回值
获取线程返回值,需要使用异步的方式得到,Boost中提供了ASIO库来实现异步操作,该库采用了前摄器设计模式,实现了可移植的异步IO操作。
首先来简单的看一下,如何使用异步的方式实现创建线程的。
#define BOOST_THREAD_VERSION 5
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <boost/function.hpp>
using namespace std;
void MyThread(int x)
{
for (int i = 0; i < x; i++)
std::cout << i << std::endl;
}
int main(int argc, char *argv[])
{
// 第一种使用方式
auto x = async(&MyThread, 10);
x.wait();
// 直接通过bind绑定参数
async(boost::bind(MyThread, 20));
// 直接使用lambda表达式
auto y = boost::async([]
{
cout << "hello lyshark" << endl;
});
y.wait();
std::system("pause");
return 0;
}
当我们需要获取单个线程的返回值时,可以使用valid()方法或使用get()将返回值从线程里拉取出来。
#define BOOST_THREAD_VERSION 5
#include <iostream>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <boost/function.hpp>
using namespace std;
int MyThread(int x, int y)
{
Sleep(3000);
return x + y;
}
typedef struct
{
int x;
int y;
}MyStruct;
MyStruct MyThreadStruct(int x, int y)
{
MyStruct ref;
ref.x = x + 100;
ref.y = y + 100;
return ref;
}
int main(int argc, char *argv[])
{
// 返回数值直接使用get得到
auto f = boost::async(boost::bind(MyThread, 10, 20));
// auto f = boost::async(boost::launch::async,bind(MyThread, 10, 20));
if (f.valid())
{
cout << "获取计算结果: " << f.get() << endl;
}
f.wait();
// 返回参数是结构体
auto t = boost::async(boost::bind(MyThreadStruct, 100, 200));
MyStruct tmp = t.get();
cout << "获取结构体参数A: " << tmp.x << " 参数B: " << tmp.y << endl;
t.wait();
std::system("pause");
return 0;
}
有时候我们会一次性创建多个线程共同执行,此时想要获取到每个线程中的返回值,那么就需要使用多个future对象,代码如下。
#define BOOST_THREAD_VERSION 5
#include <iostream>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <boost/function.hpp>
using namespace std;
int MyThread(int x, int y)
{
Sleep(3000);
return x + y;
}
int main(int argc, char *argv[])
{
std::vector<boost::future<int>> vect;
// 启动异步线程,并放入vector容器中
for (int i = 0; i < 100; ++i)
{
vect.push_back(boost::async(bind(MyThread, i, i * 10)));
}
// 等待所有线程计算结束
boost::wait_for_all(vect.begin(), vect.end());
for (auto &x : vect)
{
// 获取到返回值
if (x.valid())
{
cout << "线程计算结果: " << x.get() << endl;
}
}
std::system("pause");
return 0;
}
返回数值类型如果不够存储的话,那么我们可以定义一个MyStruct结构体,通过结构体传递参数,并将计算结果返回为结构体类型。
#define BOOST_THREAD_VERSION 5
#include <iostream>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <boost/function.hpp>
using namespace std;
typedef struct
{
int x;
int y;
}MyStruct;
// 定义一个返回结构体的函数
MyStruct MyThreadStruct(int x, int y)
{
MyStruct ref;
ref.x = x + 100;
ref.y = y + 100;
return ref;
}
int main(int argc, char *argv[])
{
std::vector<boost::future<MyStruct>> vec;
for (int x = 0; x < 100; x++)
{
vec.push_back(boost::async(bind(MyThreadStruct, x, x + 10)));
}
boost::wait_for_all(vec.begin(), vec.end());
for (auto &x : vec)
{
MyStruct tmp = x.get();
cout << "获取计算结果A: " << tmp.x << " 获取结果B: " << tmp.y << endl;
}
std::system("pause");
return 0;
}
由于future只能get获取一次数据,使得它不能被多线程并发访问,所以就出现了shared_future,它是future的增强,可以线程安全的多次调用get()获取到计算结果,修改很简单只需要将声明改一下,其他的不用动。
void Async()
{
std::vector<boost::shared_future<MyStruct>> vec;
for (int x = 0; x < 100; x++)
{
vec.push_back(boost::async(bind(MyThreadStruct, x, x + 10)).share());
}
boost::wait_for_all(vec.begin(), vec.end());
for (auto &x : vec)
{
MyStruct tmp = x.get();
cout << "获取计算结果: " << tmp.x << endl;
}
}
7.5 共享锁
shared_mutex(共享互斥锁)是 C++11 标准库中引入的一种线程同步机制,可以实现同时有多个线程同时读取共享资源,但只能有一个线程写入共享资源的机制。与常见的互斥锁不同,shared_mutex 具有更加细致的控制对共享资源的访问权限。
该锁允许线程获取多个共享所有权和一个专享所有权,实现了读写锁机制,即多个读线程一个写线程。
#define BOOST_THREAD_VERSION 5
#include <iostream>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <boost/function.hpp>
using namespace std;
using namespace boost;
class MyClass
{
private:
// 读写的数据
int m_x;
// 定义共享互斥量
boost::shared_mutex rw_mutex;
public:
MyClass()
{
m_x = 0;
}
// 写数据
void write()
{
//写锁定
boost::unique_lock<boost::shared_mutex> g(rw_mutex);
++m_x;
}
// 读数据
void read(int *x)
{
// 读锁定
boost::shared_lock<boost::shared_mutex> g(rw_mutex);
*x = m_x;
}
};
// 定义写函数,每次调用都会写入十次
void writer(MyClass &ptr)
{
for (int x = 0; x < 10; x++)
{
ptr.write();
}
}
// 定义读函数,每次调用读取十次
void reader(MyClass &ptr)
{
int item;
for (int x = 0; x < 10; x++)
{
ptr.read(&item);
std::cout << "读取数据: " << item << std::endl;
}
}
int main(int argc, char *argv[])
{
MyClass ptr;
thread_group pool;
// 定义2个读
pool.create_thread(boost::bind(reader, boost::ref(ptr)));
pool.create_thread(boost::bind(reader, boost::ref(ptr)));
// 定义1个写
pool.create_thread(boost::bind(writer, boost::ref(ptr)));
pool.join_all();
std::system("pause");
return 0;
}
7.6 获取线程ID号
实现线程池,每次将一个线程service_io存入到栈中,需要时从栈中弹出并调用内部相应的函数。
#include <stack>
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp>
using namespace std;
using namespace boost;
class ThreadPool
{
static int count;
int NoOfThread;
int counter;
thread_group group;
boost::mutex mutex_;
asio::io_service io_service;
stack<boost::thread*> thread_stack;
public:
ThreadPool(int num)
{
NoOfThread = num;
counter = 0;
boost::mutex::scoped_lock lock(mutex_);
if (count == 0)
count++;
else
return;
// 开辟线程并放入栈中存储
for (int i = 0; i<num; ++i)
{
thread_stack.push(group.create_thread(boost::bind(&asio::io_service::run, &io_service)));
}
}
~ThreadPool()
{
io_service.stop();
group.join_all();
}
// 从栈中弹出元素
boost::thread* get_thread()
{
// 判断成立说明不存在线程
if (counter > NoOfThread)
return NULL;
// 否则从栈中弹出
counter++;
boost::thread* ptr = thread_stack.top();
thread_stack.pop();
return ptr;
}
// 获取到元素计数器
int get_number()
{
return counter;
}
};
int ThreadPool::count = 0;
int main(int argc, char * argv[])
{
// 定义启动10个线程
ThreadPool pool(10);
// 循环线程池
for (int x = 0; x < 10; x++)
{
boost::thread* ptr = pool.get_thread();
cout << "线程ID: " << ptr->get_id() << endl;
int num = pool.get_number();
std::cout << "计数器: " << num << std::endl;
}
std::system("pause");
return 0;
}
4.7 C++ Boost 多线程并发库的更多相关文章
- 03.Java多线程并发库API使用2
1.多个线程之间共享数据的方式探讨 1.如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,买票系统就可以这么做. 2.如果每个线程执行的代 ...
- java——多线程并发库
JDK5中增加了Doug Lea的并发库,这一引进给Java线程的管理和使用提供了强大的便利性. java.util.current包中提供了对线程优化.管理的各项操作,使得线程的使用变得的心应手.该 ...
- 02.Java多线程并发库API使用
1. 传统线程技术回顾 继承线程与实现Runnable的差异?为什么那么多人都采取第二种方式? 因为第二种方式更符合面向对象的思维方式.创建一个线程,线程要运行代码,而运行的代码都封装到 ...
- boost的并发库
thread: http://www.boost.org/doc/libs/1_61_0/libs/thread/ asio: http://www.boost.org/doc/libs/1_61_0 ...
- 04.Java多线程并发库API使用3
1.java5的Semaphere同步工具 Semaphore可以维护当前访问自身的线程个数,并提供了同步机制.使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数 ...
- 【C/C++学院】0904-boost智能指针/boost多线程锁定/哈希库/正則表達式
boost_array_bind_fun_ref Array.cpp #include<boost/array.hpp> #include <iostream> #includ ...
- java的多线程和并发库
一.多线程基础知识 1.传统使用类Thread和接口Runnable实现 1)在Thread子类覆盖的run方法中编写运行代码 2)在传递给Thread对象的Runnable对象的run方法中编写代码 ...
- Java多线程与并发库高级应用-java5线程并发库
java5 中的线程并发库 主要在java.util.concurrent包中 还有 java.util.concurrent.atomic子包和java.util.concurrent.lock子包 ...
- 并发库应用之十 & 多线程数据交换Exchanger应用
申明:用大白话来说就是用于实现两个人之间的数据交换,每个人在完成一定的事务后想与对方交换数据,第一个先拿出数据的人会一直等待第二个人,直到第二个人拿着数据到来时,才能彼此交换数据. java.util ...
- Java多线程(三) —— 线程并发库之总体架构
对java并发库一直觉得很神秘,决定好好研究一下. 参考文献: https://blog.csdn.net/hp910315/article/details/50963095 http://www.b ...
随机推荐
- 3-3 vector 和 迭代器
1 vector 容器vector可以理解为变长数组,它里面放的是相同类型的元素. vector<int> vec={1,2,3,4};//拷贝构造 vector<string> ...
- 【django-vue】celery延迟任务、定时任务 django中使用celery 秒杀功能 双写一致性 首页轮播图定时更新 课程前端页面
目录 上节回顾 字符编码 django-redis 今日内容 1 celery 执行异步任务,延迟任务,定时任务 延时任务 定时任务 2 django中使用celery 2.1 秒杀功能 2.1.1 ...
- 语音顶会 ICASSP 2022 成果分享:基于时频感知域模型的单通道语音增强算法
近日,阿里云视频云音频技术团队与新加坡国立大学李海洲教授团队合作论文 <基于时频感知域模型的单通道语音增强算法 >(Time-Frequency Attention for Monaura ...
- CH#17C 舞动的夜晚(最大流+强连通分量)
舞动的夜晚 CH Round #17 描述 L公司和H公司举办了一次联谊晚会.晚会上,L公司的N位员工和H公司的M位员工打算进行一场交际舞.在这些领导中,一些L公司的员工和H公司的员工之间是互相认识的 ...
- SpringBoot发布https服务
一.生成SSL证书 1.进入本地jdk的路径 cd D:\Program\jdk1.8.0_77\jre\lib\security cmd窗口生成证书HSoftTiger.keystore到D盘 ke ...
- <vue 组件 4、插槽的使用>
代码结构 一. 01-slot-插槽的基本使用 1. 效果 同样的一个插槽,父组件调用的时候不同展现的内容就不同 2.代码 01-slot-插槽的基本使用.html <!DOCTYPE ...
- liunx上升级python2至python3
一.背景介绍 虚拟机的liunx系统CentOS7自带了python2.7.5,为使用方便需要换成最新的版本3.10.4 二.安装方法 1.安装升级GCC yum install -y gcc* op ...
- spring启动流程 (3) BeanDefinition详解
BeanDefinition在Spring初始化阶段保存Bean的元数据信息,包括Class名称.Scope.构造方法参数.属性值等信息,本文将介绍一下BeanDefinition接口.重要的实现类, ...
- 06-Shell内置命令
1.内置命令介绍 Shell 内置命令,就是由 Bash Shell 自身提供的命令,而不是文件系统中的可执行文件. 使用type 来确定一个命令是否是内置命令: type 命令 通常来说,内置命令会 ...
- Mybatis @Insert插入数据返回自增的主键id
mapper层 @Insert("insert into t_user (username,password,valid,create_time) values (#{username},# ...