上上篇文章演示了如何将异步调用转换为同步调用。本篇文章则演示了反过程。为何我们需要将异步调用转换为同步调用?这往往是为了获得编程的便利性——前文说过异步调用比较反人类。如果底层是异步的,转换为同步一般会损失性能。所以这种做法较少用于服务端,一般用于客户端。
如果是体位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试验看看)。