当前位置: 首页 > news >正文

【JsonCpp、Muduo、C++11】JsonCpp库、Muduo库、C++11异步操作

JsonCpp库、Muduo库、C++11异步操作

  • 1. JsonCpp库
    • 1.1 Json数据格式
    • 1.2 JsonCpp介绍
    • 1.3 JsonCpp使用
  • 2. Muduo库
    • 2.1 Muduo库常见接口介绍
      • 2.1.1 TcpServer类基础介绍
      • 2.1.2 EventLoop类基础介绍
      • 2.1.3 TcpConnection类基础介绍
      • 2.1.4 Buffer类基础介绍
      • 2.1.5 TcpClient类基础介绍
    • 2.2 Muduo库实现字典服务端
    • 2.3 Muduo库实现字典客户端
  • 3. C++11 异步操作
    • 3.1 std::future
    • 3.2 用法
      • 3.2.1 使用std::async和std::future配合
      • 3.2.2 使用std::packaged_task和std::future配合
      • 3.2.3 使用std::promise和std::future配合

1. JsonCpp库

1.1 Json数据格式

Json 是一种数据交换格式,它采用完全独立于编程语言的文本格式来存储和表示数据。

例如: 我们想表示一个同学的学生信息

C 代码表示

char *name = "xx";
int age = 18;
float score[3] = {88.5, 99, 58};

Json 表示

{"姓名" : "xx","年龄" : 18,"成绩" : [88.5, 99, 58],"爱好"{"书籍" : "西游记","运动" : "打篮球"}
}

Json 的数据类型包括对象,数组,字符串,数字等。

  • 对象:使用花括号 {} 括起来的表示一个对象
  • 数组:使用中括号 [] 括起来的表示一个数组
  • 字符串:使用常规双引号 “” 括起来的表示一个字符串
  • 数字:包括整形和浮点型,直接使用

1.2 JsonCpp介绍

Jsoncpp 库主要是用于实现 Json 格式数据的序列化和反序列化,它实现了将多个数据对象组织成为 json 格式字符串,以及将 Json 格式字符串解析得到多个数据对象的功能。

Json::Value类:中间数据存储类
如果要将数据对象进行序列化,就需要先存储到Json:Value对象中
如果要将数据传进行反序列化,就是解析后,将数据对象放入到Json:Value对象中

class Json::Value{Value &operator=(const Value &other); //Value重载了[]和=,因此所有的赋值和获取数据都可以通过Value& operator[](const std::string& key);//简单的⽅式完成val["name"] = "xx";Value& operator[](const char* key);Value removeMember(const char* key);//移除元素const Value& operator[](ArrayIndex index) const; //val["score"][0]Value& append(const Value& value);//添加数组元素val["score"].append(88); ArrayIndex size() const;//获取数组元素个数 val["score"].size();std::string asString() const;//转string string name = val["name"].asString();const char* asCString() const;//转char* char *name = val["name"].asCString();Int asInt() const;//转int int age = val["age"].asInt();float asFloat() const;//转float float weight = val["weight"].asFloat();bool asBool() const;//转 bool bool ok = val["ok"].asBool();
};

Jsoncpp 库主要借助三个类以及其对应的少量成员函数完成序列化及反序列化

  • 序列化接口

Json:StreamWriter类:用于进行数据序列化
Json:StreamWriter::write序列化函数
Json:StreamWriterBuilder类:Json:StreamWriter工厂类-用于生产Json:StreamWriter对象

class JSON_API StreamWriter {virtual int write(Value const& root, std::ostream* sout) = 0;
}
class JSON_API StreamWriterBuilder : public StreamWriter::Factory {virtual StreamWriter* newStreamWriter() const;
}
  • 反序列化接口

Json:CharReader类:反序列化类
Json:CharReader.:parse反序列化函数
Json:CharReaderBuilder:Json:CharReader工厂类-用于生产Json:CharReader对象

class JSON_API CharReader {virtual bool parse(char const* beginDoc, char const* endDoc, Value* root, std::string* errs) = 0;
}
class JSON_API CharReaderBuilder : public CharReader::Factory {virtual CharReader* newCharReader() const;
}

1.3 JsonCpp使用

序列化

#include<iostream>
#include<jsoncpp/json/json.h>
#include<string>
#include<memory>
#include<sstream>//风格一序列化
bool serializeversion1(const Json::Value& root,std::string& body)
{//1. 先实例化一个工厂类对象Json::StreamWriterBuilder swb;// 解决中文转化为Unicode格式swb["emitUTF8"] = true;//2. 通过工厂类对象生产派生类对象,并用基类对象指针指向//通过智能指针管理,不然忘记释放就会造成资源泄漏std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());//需要一个输出流std::stringstream ss;int ret = sw->write(root,&ss);if (ret != 0) {std::cout << "json serialize failed!\n";return false;}body = ss.str();return true;
}//风格二序列化
void serializeversion2(const Json::Value& root,std::string& body)
{Json::FastWriter fw;body = fw.write(root);Json::StyledWriter sw;body = sw.write(root);
}int main()
{const char *name = "张三";int age = 23; const char *sex = "男";float score[3] = {88, 77.5, 66};Json::Value student;student["姓名"] = name;student["年龄"] = age;student["性别"] = sex;//添加数组元素student["成绩"].append(score[0]);student["成绩"].append(score[1]);student["成绩"].append(score[2]);Json::Value fav;fav["书籍"] = "西游记";fav["运动"] = "打篮球";student["爱好"] = fav;std::string body;serializeversion1(student,body);serializeversion2(student,body);std::cout << body << std::endl;return 0;
}

反序列化

bool unserialize(const std::string& body,Json::Value& root)
{//1. 先实例化一个工厂类对象Json::CharReaderBuilder crb;//2. 通过工厂类对象生产派生类对象,并用基类对象指针指向//通过智能指针管理,不然忘记释放就会造成资源泄漏std::unique_ptr<Json::CharReader> cr(crb.newCharReader());std::string errs;bool ret = cr->parse(body.c_str(),body.c_str() + body.size(),&root,&errs);if(ret == false){std::cout << "json unserialize failed!\n";return false;}return true;
}bool unserialize2(const std::string& body,Json::Value& root)
{Json::Reader reader;bool ret = reader.parse(body,root);if(ret == false){std::cout << "json unserialize failed!\n";return false;}return true;
}int main()
{const char *name = "张三";int age = 23; const char *sex = "男";float score[3] = {88, 77.5, 66};Json::Value student;student["姓名"] = name;student["年龄"] = age;student["性别"] = sex;//添加数组元素student["成绩"].append(score[0]);student["成绩"].append(score[1]);student["成绩"].append(score[2]);Json::Value fav;fav["书籍"] = "西游记";fav["运动"] = "打篮球";student["爱好"] = fav;std::string body;serializeversion1(student,body);//serializeversion2(student,body);//std::cout << body << std::endl;Json::Value val;//bool ret = unserialize(body,val);bool ret = unserialize2(body,val);if(ret == false){return -1;}std::cout<<val["姓名"].asString()<<std::endl;std::cout<<val["年龄"].asUInt()<<std::endl;std::cout<<val["性别"].asString()<<std::endl;int sz = val["成绩"].size();for(int i = 0; i < sz; ++i){std::cout<<val["成绩"][i].asFloat()<<std::endl;}std::cout<<val["爱好"]["书籍"].asString()<<std::endl;std::cout<<val["爱好"]["运动"].asString()<<std::endl;return 0;
}

2. Muduo库

Muduo由陈硕大佬开发,是一个基于非阻塞IO和事件驱动的C++高并发TCP网络编程库。 它是一款基于主从Reactor模型的网络库,其使用的线程模型是one loop per thread,所谓one loop per thread指的是:

  • 一个线程只能有一个事件循环(EventLoop), ⽤于响应计时器和IO事件

  • 一个文件描述符只能由一个线程进行读写,换句话说就是一个TCP连接必须归属于某个EventLoop管理

2.1 Muduo库常见接口介绍

2.1.1 TcpServer类基础介绍

typedef std::shared_ptr<TcpConnection> TcpConnectionPtr;
typedef std::function<void(const TcpConnectionPtr &)> ConnectionCallback;
typedef std::function<void(const TcpConnectionPtr &, Buffer *,Timestamp)>MessageCallback;class InetAddress : public muduo::copyable
{
public://将ip和port构造成一个InetAddress对象InetAddress(StringArg ip, uint16_t port, bool ipv6 = false);
};class TcpServer : noncopyable
{
public://是否启动地址复用enum Option{kNoReusePort,kReusePort,};TcpServer(EventLoop *loop,const InetAddress &listenAddr,const string &nameArg,Option option = kNoReusePort);void setThreadNum(int numThreads);//设置子Reactor数量void start();//启动服务器,设置对监听套接字读事件关系/// 当⼀个新连接建⽴成功/关闭连接的回调函数void setConnectionCallback(const ConnectionCallback &cb){connectionCallback_ = cb;}/// 消息的业务处理回调函数---这是收到新连接消息的时候回调函数void setMessageCallback(const MessageCallback &cb){messageCallback_ = cb;}
};

这里启动服务器只是设置对监听套接字读事件关系,并没有启动事件监控,实际上是把socket和事件监控分开了。EventLoop负责对IO事件进行监控。

2.1.2 EventLoop类基础介绍

EventLoop负责IO事件监控

class EventLoop : noncopyable
{
public:/// Loops forever./// Must be called in the same thread as creation of the object.void loop();//启动事件监控/// Quits loop./// This is not 100% thread safe, if you call through a raw pointer,/// better to call through shared_ptr<EventLoop> for 100% safety.void quit();//退出事件监控//以下都是定时任务的处理TimerId runAt(Timestamp time, TimerCallback cb);//指定时间运行定时任务/// Runs callback after @c delay seconds./// Safe to call from other threads.TimerId runAfter(double delay, TimerCallback cb);//延迟多少秒后运行定时任务/// Runs callback every @c interval seconds./// Safe to call from other threads.TimerId runEvery(double interval, TimerCallback cb);//每隔几秒就运行一下定时任务/// Cancels the timer./// Safe to call from other threads.void cancel(TimerId timerId);//取消定时任务private:std::atomic<bool> quit_;std::unique_ptr<Poller> poller_;mutable MutexLock mutex_;std::vector<Functor> pendingFunctors_ GUARDED_BY(mutex_);
};

通过TcpServer和EventLoop就可以搭建一个服务器

2.1.3 TcpConnection类基础介绍

针对连接的通信做管理

class TcpConnection : noncopyable, public std::enable_shared_from_this<TcpConnection>
{
public:/// Constructs a TcpConnection with a connected sockfd////// User should not create this object.TcpConnection(EventLoop *loop,const string &name,int sockfd,const InetAddress &localAddr,const InetAddress &peerAddr);//判断当前连接处于什么状态     bool connected() const { return state_ == kConnected; }bool disconnected() const { return state_ == kDisconnected; }//发送数据,实际是把数据放到发送缓存区,打开写事件监控void send(string &&message); // C++11void send(const void *message, int len);void send(const StringPiece &message);// void send(Buffer&& message); // C++11void send(Buffer *message); // this one will swap data//关闭连接,实际还要去看发送缓存区是否还有数据,等把发送缓存区数据发送完了//或者出错了,才真正关闭连接void shutdown();            // NOT thread safe, no simultaneous calling//设置上下文void setContext(const boost::any &context){context_ = context;}//获取上下文const boost::any &getContext() const{return context_;}boost::any *getMutableContext(){return &context_;}//在TcpServer就已经给每个连接的TcpConnection对象设置过了void setConnectionCallback(const ConnectionCallback &cb){connectionCallback_ = cb;}void setMessageCallback(const MessageCallback &cb){messageCallback_ = cb;}private:enum StateE{kDisconnected,kConnecting,kConnected,kDisconnecting};EventLoop *loop_;ConnectionCallback connectionCallback_;MessageCallback messageCallback_;WriteCompleteCallback writeCompleteCallback_;boost::any context_;
};

2.1.4 Buffer类基础介绍

class Buffer : public muduo::copyable
{
public:static const size_t kCheapPrepend = 8;static const size_t kInitialSize = 1024;explicit Buffer(size_t initialSize = kInitialSize): buffer_(kCheapPrepend + initialSize),readerIndex_(kCheapPrepend),writerIndex_(kCheapPrepend);void swap(Buffer &rhs)//获取缓存区可读空间大小size_t readableBytes() const//获取缓存区可写空间大小size_t writableBytes() const//获取缓存区中数据的起始地址const char *peek() constconst char *findEOL() constconst char *findEOL(const char *start) constvoid retrieve(size_t len) void retrieveInt64() void retrieveInt32() void retrieveInt16() void retrieveInt8()//从缓存区取出所有数据,当作string返回,并删除缓存区中的数据string retrieveAllAsString()//从缓存区取出len长度的数据,当作string返回,并删除缓存区中的数据string retrieveAsString(size_t len) void append(const StringPiece &str) void append(const char * /*restrict*/ data, size_t len) void append(const void * /*restrict*/ data, size_t len) char *beginWrite()const char *beginWrite() constvoid hasWritten(size_t len) void appendInt64(int64_t x) void appendInt32(int32_t x) void appendInt16(int16_t x) void appendInt8(int8_t x)int64_t readInt64()//数据读取位置向后偏移4字节,本质上就是删除起始位置的4字节数据int32_t readInt32()int16_t readInt16()int8_t readInt8()int64_t peekInt64() const//尝试从缓存区获取4字节的数据,进行网络字节序转换成整型,但是数据并不从缓存区删除//查看正文长度大小,如果缓存区数据足够正文大小,就把前4字节以及后面正文都从缓存区取走//如果不够就不取走,前4个字节也正好没有取走int32_t peekInt32() constint16_t peekInt16() constint8_t peekInt8() constvoid prependInt64(int64_t x) void prependInt32(int32_t x) void prependInt16(int16_t x) void prependInt8(int8_t x) void prepend(const void * /*restrict*/ data, size_t len) private: std::vector<char> buffer_;//vector进行缓存区内存管理size_t readerIndex_;size_t writerIndex_;static const char kCRLF[];
};

2.1.5 TcpClient类基础介绍

TcpClient只是创建一个套接字,它也是通过一个EventLoop进行IO事件监控以及处理的,因此也有设置连接建立成功/连接关闭的回调函数,收到消息的回调函数。

Muduo库的IO事件都是通过EvevtLoop进行事件监控的,一旦对应连接的事件就绪了,就会调用对应连接的Connection对象里面曾经设置的读写等事件就绪的回调。假如是读事件就绪,就会先把数据读到Connection里面的Buffer缓存区,然后在调用在TcpClient设置过的新数据来了的回调函数进行处理。所以我们需要设置回到函数进行处理。并没有直接发送消息接收消息的接口。

class TcpClient : noncopyable
{
public:// TcpClient(EventLoop* loop);// TcpClient(EventLoop* loop, const string& host, uint16_t port);TcpClient(EventLoop *loop,const InetAddress &serverAddr,const string &nameArg);~TcpClient();      // force out-line dtor, for std::unique_ptr members.void connect();    // 连接服务器void disconnect(); // 关闭连接void stop();//获取客户端对应的通信连接Connection对象的接口,有了Connection就可以去发送数据了//TcpClient并没有直接提供发送接口,而是先获取客户端对应的Connection对象,在去发送数据TcpConnectionPtr connection() const{MutexLockGuard lock(mutex_);return connection_;}// 连接服务器成功时的回调函数void setConnectionCallback(ConnectionCallback cb){connectionCallback_ = std::move(cb);}// 收到服务器发送的消息时的回调函数void setMessageCallback(MessageCallback cb){messageCallback_ = std::move(cb);}private:EventLoop *loop_;ConnectionCallback connectionCallback_;MessageCallback messageCallback_;WriteCompleteCallback writeCompleteCallback_;TcpConnectionPtr connection_ GUARDED_BY(mutex_);
};
/*
需要注意的是,因为muduo库不管是服务端还是客户端都是异步操作,
TcpClient的connect是一个非阻塞接口,
对于客户端来说如果我们在连接还没有完全建立成功的时候就有可能获取客户端
对应的Connection对象,然后调用内部send发送数据,这是不被允许的!
因此我们可以使⽤内置的CountDownLatch类进⾏同步操作控制
*/
class CountDownLatch : noncopyable
{
public:explicit CountDownLatch(int count);//计数大于0则阻塞void wait(){MutexLockGuard lock(mutex_);while (count_ > 0){condition_.wait();}}//计数--,为0时唤醒waitvoid countDown(){MutexLockGuard lock(mutex_);--count_;if (count_ == 0){condition_.notifyAll();}}int getCount() const;private:mutable MutexLock mutex_;Condition condition_ GUARDED_BY(mutex_);int count_ GUARDED_BY(mutex_);
};

2.2 Muduo库实现字典服务端

#include<iostream>
#include<string>
#include<unordered_map>
#include<stdint.h>//服务器所需要的muduo库中的类
#include<muduo/net/TcpServer.h>
#include<muduo/net/EventLoop.h>
#include<muduo/net/TcpConnection.h>
#include<muduo/net/Buffer.h>class DictServer
{public:DictServer(uint16_t port):_server(&_baseloop,muduo::net::InetAddress("0.0.0.0",port),"DictServer",muduo::net::TcpServer::kReusePort){//设置连接事件(连接建立/管理)的回调_server.setConnectionCallback(std::bind(&DictServer::onConnection, this, std::placeholders::_1));//设置连接消息的回调_server.setMessageCallback(std::bind(&DictServer::onMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));}void start(){_server.start();//先开始监听_baseloop.loop();//开始死循环事件监控}private://连接建立/连接关闭回调函数void onConnection(const muduo::net::TcpConnectionPtr& conn){if(conn->connected()){std::cout<<"连接建立"<<std::endl;}else{std::cout<<"连接断开"<<std::endl;}}//业务处理回调函数void onMessage(const muduo::net::TcpConnectionPtr& conn,muduo::net::Buffer* buf, muduo::Timestamp){static std::unordered_map<std::string, std::string> dict_map = {{"hello",  "你好"},{"world",  "世界"},{"bite",  "比特"}};std::string msg = buf->retrieveAllAsString();std::string res;auto it = dict_map.find(msg);if (it != dict_map.end()) {res = it->second;}else {res = "未知单词!";}conn->send(res);}private:muduo::net::EventLoop _baseloop;muduo::net::TcpServer _server;
};int main()
{DictServer server(8080);server.start();return 0;
}

设置回调函数、设置线程数量、对监听套接字读事件关心,启动事件监控

IFLAG= -I ../build/release-install-cpp11/include/
LFLAG= -L ../build/release-install-cpp11/lib/
server:server.ccg++ $(IFLAG) $(LFLAG) -o $@ $^ -std=c++11 -lmuduo_net -lmuduo_base -lpthread

2.3 Muduo库实现字典客户端

#include<iostream>
#include<string>
#include<stdint.h>
#include<muduo/net/TcpClient.h>
#include<muduo/net/EventLoop.h>
#include<muduo/net/TcpConnection.h>
#include<muduo/net/Buffer.h>
#include<muduo/base/CountDownLatch.h>
#include<muduo/net/EventLoopThread.h>class DictClient
{public:DictClient(const std::string& ip,uint16_t port):_baseloop(_loopthread.startLoop()),_downlatch(1),_client(_baseloop,muduo::net::InetAddress(ip,port),"DictClient"){_client.setConnectionCallback(std::bind(&DictClient::onConnection,this,std::placeholders::_1));_client.setMessageCallback(std::bind(&DictClient::onMessage,this,std::placeholders::_1,std::placeholders::_2,std::placeholders::_3));}void connect(){_client.connect();_downlatch.wait();//什么时候启动IO事件监控?//事件监控内部可是一个死循环,在构造启动或者其他地方启动那就一直死循环下去//因此创建一个EventLoop对应的线程专门负责对事件进行监控}void shutdown() { _client.disconnect(); }bool send(const std::string msg){if (_conn->connected() == false) {std::cout << "连接已经断开,发送数据失败!\n";return false;}_conn->send(msg);return true;}private:void onConnection(const muduo::net::TcpConnectionPtr& conn){if(conn->connected()){std::cout<<"连接建立"<<std::endl;_downlatch.countDown();_conn = conn;}else{std::cout<<"连接断开"<<std::endl;_conn.reset();}}void onMessage(const muduo::net::TcpConnectionPtr& conn,muduo::net::Buffer* buf, muduo::Timestamp){std::string str = buf->retrieveAllAsString();std::cout<<str<<std::endl;}private://创建一个EventLoop对应的线程进行IO事件监控muduo::net::EventLoopThread _loopthread;//connect是一个非阻塞接口,防止连接还没有建立成功,就通过TcpConnection的send接口发送消息muduo::CountDownLatch _downlatch;//获取客户端对应的TcpConnectionmuduo::net::TcpConnectionPtr _conn;muduo::net::EventLoop* _baseloop;muduo::net::TcpClient _client;};int main()
{DictClient client("127.0.0.1",8080);client.connect();while(1) {std::string msg;std::cin >> msg;client.send(msg);}client.shutdown();return 0;
}

设置回调函数、发起连接

3. C++11 异步操作

3.1 std::future

std::future是C++11标准库中的一个模板类,它表示一个异步操作的结果。当我们在多线程编程中使用异步任务时,std::future可以帮助我们在需要的时候获取任务的执行结果。std::future的一个重要特性是能够阻塞当前线程,直到异步操作完成,从而确保我们在获取结果时不会遇到未完成的操作。

std::future用来表示一个异步任务的结果,或者说用于同步保存一个异步任务的结果。

同步和异步的区别:一个任务是否是当前执行流/进程自身完成的。

同步是当前执行流/进程自身完成的,异步是其他执行流/线程完成的。

应用场景

  • 异步任务:当我们需要在后台执行一些耗时操作时,如网络请求或计算密集型任务等,std::future可以用来表示这些异步任务的结果。通过将任务与主线程分离,我们可以实现任务的并行处理,从而提高程序的执行效率
  • 并发控制: 在多线程编程中,我们可能需要等待某些任务完成后才能继续执行其他操作。通过使用std::future,我们可以实现线程之间的同步,确保任务完成后再获取结果并继续执行后续操作
  • 结果获取:std::future提供了一种安全的方式来获取异步任务的结果。我们可以使用std::future::get()函数来获取任务的结果,此函数会阻塞当前线程,直到异步操作完成。这样,在调用get()函数时,我们可以确保已经获取到了所需的结果

3.2 用法

std:future并不能单独使用,需要搭配一些能够执行异步任务的模板类或函数一起使用。

3.2.1 使用std::async和std::future配合

std::async是一种将任务与std::future关联的简单方法。它创建并运行一个异步任务,并返回一个与该任务结果关联的std::future对象用于获取函数结果。默认情况下,std::async是否启动一个新线程,或者在等待future时,任务是否同步运行都取决于你给的参数。这个参数为std::launch类型:

  • std::launch::deferred 表明该函数会被延迟调用,直到在future上调用get()或者wait()才会开始执行任务
  • std::launch::async 表明会创建一个线程去运行函数
  • std::launch::deferred | std::launch::async 内部通过系统等条件自动选择策略
#include<iostream>
#include<future>int Add(int x, int y)
{return x + y;
}int main()
{//std::launch::async策略:内部创建一个线程执行函数,函数运行结果通过future获取//std::launch::deferred策略:同步策略,获取结果的时候再去执行任务std::future<int> ret = std::async(std::launch::async,Add,11,22);std::future<int> ret = std::async(std::launch::deferred,Add,11,22);//std::future<int>::get()  用于获取异步任务的结果,如果还没有结果就会阻塞std::cout<<ret.get()<<std::endl;return 0;
}

3.2.2 使用std::packaged_task和std::future配合

std::packaged_task就是将任务和std::feature绑定在一起的类模板,是一种对任务的封装。我们可以通过std::packaged_task对象调用get_future()方法获取任务相关联的std::feature对象。std::packaged_task的模板参数是函数签名。

std::packaged_task类模板:为一个函数生成一个异步任务对象(可调用对象),用于在其他线程中执行

可以把std::future和std::async看成是分开的, 而 std::packaged_task则是一个整体

#include<iostream>
#include<future>
#include<thread>
#include<memory>int Add(int x, int y)
{return x + y;
}int main()
{// 1.封装任务// std::packaged_task<int(int, int)> task(add);// 此处可执⾏其他操作, ⽆需等待// std::cout << "hello bit!" << std::endl;// 2.获取任务包关联的future对象// std::future<int> result_future = task.get_future();// 3.执行任务// std::thread t([&]{//     task(11,22);// });// 4.获取结果// std::cout<<res.get()<<std::endl;// t.join();// 需要注意的是,task虽然重载了()运算符,但task并不是⼀个函数,// std::async(std::launch::async, task, 1, 2); //--错误用法// 所以导致它作为线程的入口函数时,语法上看没有问题,但是实际编译的时候会报错// std::thread(task, 1, 2); //---错误用法// ⽽packaged_task禁⽌了拷贝构造// 且因为每个packaged_task所封装的函数签名都有可能不同,因此也⽆法当作参数⼀样传递// 传引用不可取,毕竟任务在多线程下执⾏存在局部变量声明周期的问题,因此不能传引用// 因此想要将⼀个packaged_task进⾏异步调用// 简单⽅法就只能是new packaged_task,封装函数传地址进⾏解引用调用了// ⽽类型不同的问题,在使用的时候可以使用类型推导来解决// 1.封装任务auto task = std::make_shared<std::packaged_task<int(int,int)>>(Add);// 2.获取任务包关联的future对象std::future<int> res = task->get_future();// 3.执行任务std::thread t([&]{(*task)(11,22);});// 4.获取结果std::cout<<res.get()<<std::endl;t.join();return 0;
}

3.2.3 使用std::promise和std::future配合

std::promise提供了一种设置值的方式,它可以在设置之后通过相关联的std::future对象进行读取。换种说法就是之前说过std::future可以读取一个异步函数的返回值了, 但是要等待就绪, 而std::promise就提供一种方式手动让 std::future就绪。

std:promise类模板:实例化的对象可以返回一个future,在其他线程中向promise对象设置数据,其他线程的关联future就可以获取数据。

#include<iostream>
#include<future>
#include<thread>int Add(int x,int y)
{return x + y;
}int main()
{//1.在使用的时候,就是先实例化一个指定结果的promise对象std::promise<int> pro;//2.通过promise对象,获取关联的future对象std::future<int> res = pro.get_future();//3.在任意位置给promise设置数据,就可以通过关联的future获取到这个设置的数据了std::thread thr([&]{int sum = Add(11,22);pro.set_value(sum);});std::cout<<res.get()<<std::endl;thr.join();return 0;
}

相关文章:

【JsonCpp、Muduo、C++11】JsonCpp库、Muduo库、C++11异步操作

JsonCpp库、Muduo库、C11异步操作 1. JsonCpp库1.1 Json数据格式1.2 JsonCpp介绍1.3 JsonCpp使用 2. Muduo库2.1 Muduo库常见接口介绍2.1.1 TcpServer类基础介绍2.1.2 EventLoop类基础介绍2.1.3 TcpConnection类基础介绍2.1.4 Buffer类基础介绍2.1.5 TcpClient类基础介绍 2.2 M…...

【自然语言处理与大模型】LlamaIndex的词嵌入模型和向量数据库

&#xff08;1&#xff09;嵌入模型的定义和作用 嵌入模型&#xff08;Embedding Model&#xff09;的本质在于将高维的、稀疏的数据转换为低维的、密集的向量表示&#xff0c;使得这些向量能够在数学空间中表达原始数据的语义信息。作用如下&#xff1a; 降维表示&#xff1a;…...

SLAM算法工程师面经大全:2025年面试真题解析与实战指南

SLAM算法工程师面经大全&#xff1a;2025年面试真题解析与实战指南 一、SLAM技术概述与核心原理 1&#xff0e;SLAM基础理论框架 SLAM算法的核心在于同步定位与建图&#xff0c;这一过程涉及传感器数据融合、运动建模与优化方法。在传感器数据融合方面&#xff0c;不同类型的…...

虚拟现实视频播放器 2.6.1 | 支持多种VR格式,提供沉浸式观看体验的媒体播放器

虚拟现实媒体播放器是一款专为在智能手机上播放VR&#xff08;虚拟现实&#xff09;照片和视频而设计的应用程序。它支持多种格式的影像内容&#xff0c;包括360和180等距矩形柱面、标准镜头和鱼眼镜头拍摄的照片和视频&#xff0c;并且兼容3D立体并排、上/下以及收缩媒体格式。…...

从黔西游船侧翻事件看极端天气预警的科技防线——疾风气象大模型如何实现精准防御?

近日,贵州省黔西市一起载人游船侧翻事故令人痛心。调查显示,事发时当地突遇强风暴雨,水面突发巨浪导致船只失控。这一事件再次凸显:在极端天气频发的时代,传统“经验式防灾”已不足够,唯有依靠智能化的气象预警技术,才能筑牢安全底线。 极端天气预警的痛点:为什么传统方…...

颠覆监测体验!WM102无线温湿度记录仪开启智能物联新时代

当温湿度失控成为企业隐形成本杀手&#xff0c;您是否还在用传统设备疲于应对&#xff1f;一款集智能、精准、全能于一身的神器横空出世——WM102无线温湿度记录仪&#xff0c;用硬核科技重新定义环境监测&#xff01; ▌5大场景痛点 一招破解 无论是医药冷库里的疫苗守护战&am…...

Linux云服务器配置git开发环境

文章目录 1. 安装 git2. git clone3. git add .4. git commit -m 提交记录5. git push&#x1f351; 异常原因&#x1f351; 解决办法 6. git pull7. git log8. git rm9. git mv10. git status 1. 安装 git sudo yum install git -y2. git clone 此命令的作用是从远程仓库把代…...

山东大学软件学院项目实训-基于大模型的模拟面试系统-个人主页头像上传

采用图床的方案&#xff0c;存储用户头像。 实现步骤 引入OSS依赖 在我们的SpringBoot项目中引入OSS依赖 <dependency><groupId>com.aliyun.oss</groupId><artifactId>aliyun-sdk-oss</artifactId><version>3.10.2</version> &l…...

AI智能体|扣子(Coze)实战【天气查询插件开发教程】

大家好&#xff0c;我是偶然&#xff0c;AI洞察&#xff0c;AI智能体&#xff0c;AI实战案例分享 今天教大家用 Coze 开发一个天气插件&#xff0c;本来我是想教大家怎么用 AI 写代码节点实现节点功能的&#xff0c;但是我感觉太复杂了。 起码来说&#xff0c;我还没做到用特别…...

VBA ListBox/ComboBox 响应鼠标滚轮操作

一般情况下&#xff0c;在Excel的vba窗口中&#xff0c;我们是无法使用鼠标滚轮控制例如 ListBox 和 ComboBox 控件的。我们只能通过鼠标点击的方式来逐个选择选项。 我们都知道&#xff0c;通过鼠标滚轮快速的上下滚动候选项&#xff0c;以快速的定位选择选项&#xff0c;这是…...

Linux 更改内存交换 swap 为 zram 压缩,减小磁盘写入

1、查看当前 swap 的方式 swapon --show 我这里是默认的 swap 文件&#xff0c;大小为 2G。 2、安装 zram Ubuntu 下&#xff1a; sudo apt install zram-tools安装后默认会启动&#xff1a; 3、关闭默认的 swap 文件 sudo swapoff /swapfile 其次是关闭 /etc/fstab 中的 …...

TypeScript简介

&#x1f31f; TypeScript入门 TypeScript 是 JavaScript 的超集&#xff0c;由微软开发并维护&#xff0c;通过静态类型检查和现代语言特性&#xff0c;让大型应用开发变得更加可靠和高效。 // 一个简单的 TypeScript 示例 interface User {name: string;age: number;greet():…...

Facebook如何运用AI实现元宇宙的无限可能?

引言 元宇宙&#xff08;Metaverse&#xff09;是一个虚拟的、由多个 3D 虚拟世界组成的网络&#xff0c;用户可以在其中进行互动、游戏、工作和社交。Facebook 作为全球最大的社交媒体平台之一&#xff0c;对元宇宙的构建和实现有着深远的影响。通过运用人工智能&#xff08;…...

【大语言模型ChatGPT+Python】近红外光谱数据分析及机器学习与深度学习建模(近红外光谱数据分析、定性/定量分析模型代码自动生成等)

近红外光谱数据分析是一种重要的分析技术&#xff0c;广泛应用于化学、食品、制药、农业、环境科学等领域。以下是关于近红外光谱数据分析的详细介绍&#xff1a; 一、基本原理 近红外光谱的范围 近红外光谱是指波长范围在780 - 2500纳米的电磁辐射。在这个波段&#xff0c;分子…...

ArcGIS Pro图斑属性自动联动更新-在线卫星底图图斑采集

今天介绍一下在ArcGIS Pro图斑属性自动联动更新 主要介绍内容&#xff1a; 1、ArcGIS Pro数据设计中属性规则的设置&#xff0c;实现图斑属性的自动更新与联动更新。 2、介绍ArcGIS Pro不能新建要素类图层的原因 3、包括新建字段等内容 4、deepseek查询arcade计算图斑面积语…...

OpenHarmony GPIO应用开发-LED

学习于&#xff1a; https://docs.openharmony.cn/pages/v5.0/zh-cn/device-dev/driver/driver-platform-gpio-develop.md https://docs.openharmony.cn/pages/v5.0/zh-cn/device-dev/driver/driver-platform-gpio-des.md 通过OpenHarmony官方文档指导可获知&#xff1a;芯片厂…...

C语言结构体内存对齐使用场景

#pragma pack(push, 1) 和 #pragma pack(pop) 的使用场景&#xff08;C语言&#xff09; 这两个预处理指令用于控制结构体成员的内存对齐方式&#xff0c;主要在以下场景中使用&#xff1a; 主要使用场景 网络通信协议处理 当需要精确控制结构体布局以匹配网络协议格式时 确…...

极速轻量,Rust 网络开发新选择:Hyperlane 框架深度解析

极速轻量&#xff0c;Rust 网络开发新选择&#xff1a;Hyperlane 框架深度解析 在高性能网络服务开发领域&#xff0c;Rust 凭借其内存安全与高效并发的特性备受青睐。今天&#xff0c;我们迎来一款专为现代 Web 服务打造的明星框架——Hyperlane&#xff0c;它以“轻量高效、…...

从零开始学习人工智能(Python高级教程)Day6-Python3 正则表达式

一、Python3 正则表达式 正则表达式是一个特殊的字符序列&#xff0c;它能帮助你方便的检查一个字符串是否与某种模式匹配。 在 Python 中&#xff0c;使用 re 模块来处理正则表达式。 re 模块提供了一组函数&#xff0c;允许你在字符串中进行模式匹配、搜索和替换操作。 r…...

UKCC(原OUCC)真题讲解(一)

题目链接&#xff1a;PRACTICE - 2025 Coding Challenge - UK Bebras 1.GreenStar 方法&#xff1a;使用模块 参考答案&#xff1a; 【知识点】 这里的长度100&#xff0c;是指中心到角的距离。 2.Draw a Square 参考答案&#xff1a; 【知识点】 在正多边形的图案中&#…...

Linux——特殊权限管理

Linux中的特殊权限&#xff08;setuid、setgid、sticky bit&#xff09;扩展了基本的文件权限机制&#xff0c;提供了更精细的控制。以下是详细说明&#xff1a; 1. SetUID&#xff08;SUID&#xff09; 作用&#xff1a;允许用户以文件所有者的权限执行文件。 例如&#xff1a…...

Ubuntu18.04搭建samda服务器

一.什么是Samba服务器&#xff1f; Samba服务器是一种基于开源协议实现的网络共享服务软件&#xff0c;主要用于在不同操作系统&#xff08;如Windows、Linux、Unix&#xff09;之间实现文件和打印机共享功能。其核心目标是解决跨平台资源共享的兼容性问题&#xff0c;尤其是在…...

vue3搭建后台管理系统

找menu菜单 上部用height设置高度和背景颜色 需要自己改左侧栏的边线 将el-menu的border设置为0,然后上方设置border-right设置边框 设置右边 创建data.vue input组件 导入icon图标 引入import 一个template 设置个card el-card 定义card 加el-button查询 el-table表单 …...

[学习]RTKLib详解:pntpos.c与postpos.c

文章目录 RTKLib详解&#xff1a;pntpos.c与postpos.cPart A: pntpos.c一、概述二、整体工作流程三、主要函数说明1. pntpos()2. satposs()3. estpos()4. rescode()5. prange()6. ionocorr()7. tropcorr()8. valsol()9. raim_fde()10. estvel() 四、函数调用关系图&#xff08;…...

64.微服务保姆教程 (七) RocketMQ--分布式消息中间件

RocketMQ–分布式消息中间件 一、MQ 1、什么是MQ MQ(Message Queue)消息队列,是基础数据结构中“先进先出”的一种数据结构。指把要传输的数据(消息)放在队列中,用队列机制来实现消息传递——生产者产生消息并把消息放入队列,然后由消费者去处理。消费者可以到指定队…...

邀约媒体,邀请到场 多少钱?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体胡老师。 在策划线下活动时&#xff0c;媒体邀约是提升品牌曝光度和影响力的重要环节。不同类型和规模的媒体邀约&#xff0c;其预算需求也各不相同。以下为各类媒体邀约的费用概览及影响因素&#…...

Android数据库全栈开发实战:Room+SQLCipher+Hilt企业级应用构建

简介 在移动应用开发中,数据库作为数据存储的核心组件,其安全性和性能对企业级应用至关重要。本文将从零开始,全面讲解Android数据库开发的最新技术,包括Room框架的深度使用、SQLCipher加密数据库的实现、Hilt依赖注入的集成以及前后端数据同步的完整方案。通过一个加密任…...

Kafka与RocketMQ在事务消息实现上的区别是什么?

一、Kafka事务消息核心实现&#xff08;基于2.8版本&#xff09; // KafkaProducer.java public synchronized Future<RecordMetadata> send(ProducerRecord<K, V> record) {// 事务消息校验&#xff08;第256行&#xff09;if (transactionManager ! null &&…...

Maven 依赖发布与仓库治理

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…...

hybird接口配置

【sw1】 [sw1]vlan batch 10 20 [sw1]int g 0/0/3 [sw1-GigabitEthernet0/0/1]port hybrid tagged vlan 10 20 [sw1-GigabitEthernet0/0/1]int g 0/0/1 [sw1-GigabitEthernet0/0/2]port hybrid pvid vlan 10 [sw1-GigabitEthernet0/0/2]port hybrid untagged vlan 10 20 …...

AI视频智能分析网关打造社区/工厂/校园/仓库智慧消防实现精准化安全管控

一、背景 随着社区、商业场所对消防安全要求日益提升&#xff0c;传统消防系统已难以满足智能化、精细化管理需求。智能分析网关融合物联网与人工智能技术&#xff0c;具备强大的数据处理与分析能力&#xff0c;可全面升级消防系统。将其融入消防系统各层级&#xff0c;搭建智…...

Web3 应用中常见的数据安全风险及防护措施

随着 Web3 技术的蓬勃发展&#xff0c;我们见证了一个全新的互联网时代的到来。Web3 应用以其去中心化、用户控制数据和透明性的特点&#xff0c;为用户提供了前所未有的体验。然而&#xff0c;这些应用在提供便利的同时&#xff0c;也带来了一系列数据安全风险。本文将探讨 We…...

免费视频压缩软件

一、本地软件&#xff08;支持离线使用&#xff09; 1. HandBrake 平台&#xff1a;Windows / macOS / Linux 特点&#xff1a;开源免费&#xff0c;支持多种格式转换&#xff0c;提供丰富的预设选项&#xff08;如“Fast 1080p”快速压缩&#xff09;&#xff0c;可自定义分…...

Java实用注解篇: @JSONField

前言 在 Java 开发中&#xff0c;进行对象与 JSON 的相互转换是一项常见操作&#xff0c;尤其在前后端分离的架构中显得尤为重要。Fastjson 作为阿里巴巴开源的 JSON 处理框架&#xff0c;因其高性能和强大功能而被广泛使用。JSONField 是 Fastjson 提供的一个注解&#xff0c;…...

浔川AI 第二次内测报告

浔川AI 第二次内测报告 执行社团&#xff1a;浔川社团官方联合会、总社团联合会 同意执行社团&#xff1a;总社团联合会 合作社团&#xff1a;暮烟社团官方联合会 合作分社团&#xff1a;浔川AI分社团、浔川AI分部 被执行内测程序&#xff1a;浔川AI 内测第二代 被执行内…...

React Hooks 深入浅出

目录 引言&#xff1a;React Hooks 的革命基础 Hooks useState&#xff1a;状态管理的新方式useEffect&#xff1a;组件生命周期的替代方案useContext&#xff1a;简化 Context API 额外的 Hooks useReducer&#xff1a;复杂状态逻辑的管理useCallback 与 useMemo&#xff1a;…...

解释 NestJS 的架构理念(例如,模块化、可扩展性、渐进式框架)

一、模块化设计 // user.module.ts Module({controllers: [UserController], // 当前模块的控制器providers: [UserService], // 当前模块的服务exports: [UserService] // 暴露给其他模块使用的服务 }) export class UserModule {}// order.module.ts Module({…...

Caffeine快速入门

依赖 <dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId><version>3.2.0</version> </dependency> Cache的基本api操作 Caffeine.newBuilder.build来构建Caffeine .maximumS…...

【踩坑记录】项目Bug分析:一次因 `String.isBlank()` 引发的崩溃(No such instance method: ‘isBlank‘)

项目Bug分析&#xff1a;一次因 String.isBlank() 引发的崩溃 一、前言 在日常的 Java 项目开发中&#xff0c;使用 String 的常见工具方法如 isEmpty()、trim() 等已司空见惯。然而&#xff0c;近期在一次项目中使用了 String.isBlank() 方法&#xff0c;结果竟然直接导致崩…...

SpringBoot整合Kafka、Flink实现流式处理

引言 在当今大数据处理领域&#xff0c;实时数据流处理变得越来越重要。Apache Kafka作为一个高吞吐量的分布式流处理平台&#xff0c;结合Apache Flink这一强大的流处理框架&#xff0c;可以构建出高效的实时数据处理系统。本文将指导您如何在SpringBoot应用中整合Kafka和Fli…...

互联网大厂Java求职面试:云原生与AI融合下的系统设计挑战-2

互联网大厂Java求职面试&#xff1a;云原生与AI融合下的系统设计挑战-2 第一轮提问&#xff1a;云原生架构选型与微服务治理 面试官&#xff08;技术总监&#xff09;&#xff1a;郑薪苦&#xff0c;我们先从一个基础问题开始。你了解Spring Cloud和Kubernetes在微服务架构中…...

AI算力产业领域产品全景图:从硬件基础到应用场景

目录 1、硬件产品 2、 软件产品 3、云服务产品 4、边缘计算产品 5、AI应用产品 6、AI安全产品 7、AI合规产品 8、AI教培产品 9、AI研创产品 10、AI生态产品 在人工智能迅猛发展的今天,算力已成为推动AI技术进步与应用落地的核心驱动力。随着深度学习模型规模的不断膨…...

【优选算法 | 模拟】探索模拟算法: 编程与问题分析的双重 考验

算法相关知识点可以通过点击以下链接进行学习一起加油&#xff01;双指针滑动窗口二分查找前缀和位运算 在本篇文章中&#xff0c;我们将深入解析模拟算法的原理。从基础概念到实际应用&#xff0c;带你了解如何通过模拟算法高效解决各种问题。无论你是刚接触算法的新手&#x…...

根据蓝牙名称自动匹配对应 UI

要实现“根据蓝牙名称自动匹配对应 UI”&#xff0c;并且支持未来不断增加的按摩椅型号和UI&#xff0c;推荐采用插件式UI注册自动路由的架构。下面是详细的可执行方案&#xff0c;适合你当前的 Flutter 项目结构&#xff1a; 1. 目录结构设计 假设每个按摩椅型号有独立的UI页…...

【25软考网工】第五章(7)路由协议、静态与默认路由、路由协议分类

目录 一、路由协议 1. 路由 2. 路由器工作原理 3. 查看路由表 4. IP路由查找的最长匹配原则 1&#xff09;例题#最长匹配原则示例题 5. 应用案例 1&#xff09;例题#路由优先级判断 2&#xff09;例题#路由信息内容 3&#xff09;例题#路由表迭代与静态路由 4&#…...

Rice Science∣武汉大学水稻研究团队发现水稻壁相关激酶OsWAKg16和OsWAKg52同时调控水稻抗病性和产量

近日&#xff0c;农学领域国际期刊Rice Science在线发表了武汉大学杂交水稻全国重点实验室范峰峰博士题为“Identification and Characterization of WAKg Genes Involved in Rice Disease Resistance and Yield”的研究论文。该论文系统分析了水稻壁相关激酶中包含半乳糖醛酸结…...

Spark,所用几个网页地址

hadoop的三大组成&#xff1a; 1. HDFS&#xff1a;存储。文件上传&#xff0c;下载 2. MapReduce&#xff1a;计算。词频统计&#xff0c;流量统计 3. YARN&#xff1a;调度 History Server网址&#xff1a;192.168.56.100:18080HDFS的NameNode网址&#xff1a;http://hadoop1…...

K8S PV 与 PVC 快速开始、入门实战

假设有如下三个节点的 K8S 集群&#xff1a; ​ k8s31master 是控制节点 k8s31node1、k8s31node2 是工作节点 容器运行时是 containerd 一、什么是 PV 与 PVC 1.1、什么是 PV&#xff08;PersistentVolume 持久卷&#xff09; PV 是集群中由管理员配置的一段网络存储&#xf…...

5月6(信息差)

一、经济与贸易 中美关税谈判进展 美方近期多次主动向中方传递谈判信号,中方回应称“谈的大门始终敞开”,但强调美方需先取消单边加征关税等错误做法78。 美国第一季度GDP环比下降0.3%,为2022年第二季度以来新低,经济压力或推动其加快谈判进程78。 全球贸易政策变动 特朗普…...

购物|电商购物小程序|基于微信小程序的购物系统设计与实现(源码+数据库+文档)

电商购物小程序 目录 基于微信小程序的购物系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、用户前台功能实现 2、管理员后台功能实现 四、数据库设计 1、实体ER图 2、具体的表设计如下所示&#xff1a; 五、核心代码 六、论文参考 七、最新计算机毕设选题…...