如何将异步调用转换成同步调用

本文介绍两种异步调用转换为同步调用的方法,并通过示例代码详细解释其原理及注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    上上篇文章演示了如何将异步调用转换为同步调用。本篇文章则演示了反过程。为何我们需要将异步调用转换为同步调用?这往往是为了获得编程的便利性——前文说过异步调用比较反人类。如果底层是异步的,转换为同步一般会损失性能。所以这种做法较少用于服务端,一般用于客户端。
    如果是体位2的异步,很简单,反复查询直到OK即可,两次查询间sleep。一般sleep时间设置为可以容忍的延迟时间。sleep时间短,则响应快(但即使你设置为sleep(0)也不可能每秒抽插超过1000次),但是cpu负载也比较大。注意如果不sleep,cpu将跑满,这可能导致工作线程更慢完成(想象下你让某人做件事,然后在一旁不停地问好了没好了没),最好不要这么做。
===============代码的分割线====================
#include <iostream>
#include <string>
#include <queue>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <windows.h>    //    只用到Sleep函数

using namespace std;

string somefuncMaybeBlock(){
    string str;
    getline(cin, str);
    return str;    //    如果是未采用隐式数据共享的std实现,这句话比较低效,但无妨,此例只是演示体位变换
}
class CheckMeLaterCaller{
public:
    class Result{
    public:
        Result()
            : ready_flag(false){
        }
        void setData(const string& res){
            boost::mutex::scoped_lock lock(mtx);
            result = res;
            ready_flag = true;
        }
        string getData(){
            boost::mutex::scoped_lock lock(mtx);
            return result;
        }
        bool ready(){
            boost::mutex::scoped_lock lock(mtx);
            return ready_flag;
        }
    private:
        string result;
        bool ready_flag;
        boost::mutex mtx;
    };
    void call(Result* result){
        boost::mutex::scoped_lock(mtx);
        queue.push(result);
        if (queue.size() == 1){
            cond.notify_all();
            
    }
    CheckMeLaterCaller(){
        running_flag = true;
        grp.create_thread(boost::bind(&CheckMeLaterCaller::run, this));
    }
    ~CheckMeLaterCaller(){
        {
            boost::mutex::scoped_lock lock(mtx);
            running_flag = false;
            cond.notify_all();
        }
        grp.join_all();
    }
private:
    void run(){
        while (true){
            Result* res = NULL;
            {
                boost::mutex::scoped_lock lock(mtx);
                while (running_flag && queue.empty()){
                    cond.wait(lock);              
                }
                if (!running_flag){
                    return;
                }
                res = queue.front();
                queue.pop();
            }
            if (res){
                string tmp = somefuncMaybeBlock();
                res->setData(tmp);
                    
        }
        
    boost::mutex mtx;
    boost::condition_variable cond;
    std::queue<Result*> queue;
    bool running_flag;
    boost::thread_group grp;
};

#define SOME_DELAY_YOU_CAN_BEAR 10
class BlockAgainCaller{
public:
    BlockAgainCaller(){}

    string call(){ 
        CheckMeLaterCaller::Result res;
        inner.call(&res);
        while (!res.ready()){
            ::Sleep(SOME_DELAY_YOU_CAN_BEAR);
        }
        return res.getData();
    }
private:
    CheckMeLaterCaller inner;  
};
int main(){
    BlockAgainCaller cal;
    for (int i = 0; i < 5; i ++){
        cout << cal.call() << endl;
    }
    return 0;
}
===============代码的分割线====================
    如果是体位3的异步,做法也很简单,一个bool变量+条件变量即可。下面代码中ProactiveCaller::call是一个异步调用(由somefuncMaybeBlock同步调用转换得到),BlockAgainCaller::call将其再次转换成同步调用。
===============代码的分割线====================
#include <iostream>
#include <string>
#include <queue>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <windows.h>    //    只用到Sleep函数

using namespace std;

string somefuncMaybeBlock(){
    string str;
    getline(cin, str);
    return str;    //    如果是未采用隐式数据共享的std实现,这句话比较低效,但无妨,此例只是演示体位变换
}

class ProactiveCaller{
public:
    class DoneHandler
    {
    public:
        virtual void onFinish(const string& str) = 0;
        virtual ~DoneHandler(){}
    };
    void call(DoneHandler* handler){
        boost::mutex::scoped_lock(mtx);
        queue.push(handler);
        if (queue.size() == 1){
            cond.notify_all();
             
     
    ProactiveCaller(){
        running_flag = true;
        grp.create_thread(boost::bind(&ProactiveCaller::run, this));
    }
    ~ProactiveCaller(){
        {
            boost::mutex::scoped_lock lock(mtx);
            running_flag = false;
            cond.notify_all();
        }
        grp.join_all();
    }
private:
    void run(){
        while (true){
            DoneHandler* handler = NULL;
            {
                boost::mutex::scoped_lock lock(mtx);
                while (running_flag && queue.empty()){
                    cond.wait(lock);               
                }
                if (!running_flag){
                    return;
                }
                handler = queue.front();
                queue.pop();
            }
            if (handler){
                string tmp = somefuncMaybeBlock();
                handler->onFinish(tmp);
                     
        }
     
   
    boost::mutex mtx;
    boost::condition_variable cond;
    std::queue<DoneHandler*> queue;
    bool running_flag;
    boost::thread_group grp;   
};

class BlockAgainCaller : public ProactiveCaller::DoneHandler{
public:
    BlockAgainCaller():done_flag(false){}

    string call(){       
        boost::mutex::scoped_lock lock(mtx);
        done_flag = false;       
        inner.call(this);
        while (!done_flag)
        {
            cond.wait(lock);           
        }
        return buf;
    }

    virtual void onFinish(const string& str){
        boost::mutex::scoped_lock lock(mtx);
        buf = str;
        done_flag = true;
        cond.notify_all();
    }

private:
    ProactiveCaller inner;
    boost::mutex mtx;
    boost::condition_variable cond;
    std::string buf;
    bool done_flag;
};

int main(){
    BlockAgainCaller cal;
    for (int i = 0; i < 5; i ++){
        cout << cal.call() << endl;
    }
    return 0;
}
===============代码的分割线====================
    体位2的异步转同步简单,但是要注意每秒抽插数能否满足你的要求(小于1000次)。体位3的异步转同步,每秒抽插数的极限就高多了,一般可以达到每秒几十万-百万次这个级别(取决于所使用的条件变量的效率,可以写一个啥都不干的somefuncMaybeBlock试验看看)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值