寫過CTP的同學(xué)可能不多,這是一個期貨接口。沒聽說過的也無妨。
C++多線程回調(diào)
CTP 提供了若干個父類供開發(fā)者繼承,里面的回調(diào)都是通過覆蓋父類的純虛函數(shù)實(shí)現(xiàn)。
當(dāng)SDK有事件發(fā)生的時候,就會調(diào)用這些定義的回調(diào)函數(shù)。
class CThostFtdcTraderSpi
{
public:
virtual void OnFrontConnected(){};
virtual void OnFrontDisconnected(int nReason){};
編寫一個這樣的程序是十分痛苦的,因?yàn)榛卣{(diào)函數(shù)的執(zhí)行是在某個工作線程中。所以很容易引起并發(fā)讀寫的問題。代碼會變得十分復(fù)雜。
編寫過Node.js的同學(xué)一定以及十分習(xí)慣Node的單線程模式,回調(diào)函數(shù)執(zhí)行的時候雖然有點(diǎn)“不同步”,但好歹是在一個線程中,所以定義域里面的變量可以隨便使用。用慣這種方便的編程方式的同學(xué),如果去接觸一下C++那種多線程回調(diào),一定會抓狂的。
那么如何讓CTP開發(fā)也能很舒服呢?或者干脆將CTP封裝成Node的原生模塊,然后在Node中調(diào)用,豈不妙哉。
這時候協(xié)調(diào)C++多線程和Nodejs單線程的關(guān)鍵角色就登場了,這就是libuv。
#include <uv.h>
uv_async_t async_t;
uv_async_init(uv_default_loop(),&async_t,NULL);
我們可以初始化一個默認(rèn)的事件循環(huán)。
然后我們可以把所有的回調(diào)虛函數(shù)都用下面的方式去實(shí)現(xiàn)
void uv_trader::OnFrontConnected() {
CbRtnField* field = new CbRtnField();
field->eFlag = T_ON_CONNECT;//FrontConnected
field->work.data = field;
uv_queue_work(uv_default_loop(), &field->work, _on_async, _on_completed);
}
其中_on_async是個空函數(shù),忽略。_on_completed函數(shù)回在事件循環(huán)的時候觸發(fā),保證在主線程中調(diào)用。然后我們在這個函數(shù)再去調(diào)用js的函數(shù)。
void uv_trader::_on_completed(uv_work_t * work,int){
CbRtnField* cbTrnField = static_cast<CbRtnField*>(work->data);
std::map<int, WrapTrader*>::iterator it = cb_map.find(cbTrnField->eFlag);
if (it != cb_map.end()) {
cb_map[cbTrnField->eFlag]->FunCallback(cbTrnField);
}
if (cbTrnField->rtnField)
delete cbTrnField->rtnField;
if (cbTrnField->rspInfo)
delete cbTrnField->rspInfo;
delete cbTrnField;
}
除了釋放資源,上面代碼就是從一個存儲著js回調(diào)函數(shù)的map中找到對應(yīng)的js函數(shù)進(jìn)行調(diào)用。
這些js函數(shù)都是在事先注冊好的,就在nodejs中。
trader.on("connect",function(result){
console.log("on connected");
trader.reqUserLogin('','','',function(result,iRequestID){
console.log('login return val is '+result);
});
});
js代碼寫起來就舒服多了。