寫過CTP的同學可能不多,這是一個期貨接口。沒聽說過的也無妨。
C++多線程回調
CTP 提供了若干個父類供開發者繼承,裏面的回調都是通過覆蓋父類的純虛函數實現。 當SDK有事件發生的時候,就會調用這些定義的回調函數。
class CThostFtdcTraderSpi
{
public:
virtual void OnFrontConnected(){};
virtual void OnFrontDisconnected(int nReason){};
編寫一個這樣的程序是十分痛苦的,因爲回調函數的執行是在某個工作線程中。所以很容易引起併發讀寫的問題。代碼會變得十分複雜。
編寫過Node.js的同學一定以及十分習慣Node的單線程模式,回調函數執行的時候雖然有點“不同步”,但好歹是在一個線程中,所以定義域裏面的變量可以隨便使用。用慣這種方便的編程方式的同學,如果去接觸一下C++那種多線程回調,一定會抓狂的。
那麼如何讓CTP開發也能很舒服呢?或者乾脆將CTP封裝成Node的原生模塊,然後在Node中調用,豈不妙哉。
這時候協調C++多線程和Nodejs單線程的關鍵角色就登場了,這就是libuv。
#include <uv.h>
uv_async_t async_t;
uv_async_init(uv_default_loop(),&async_t,NULL);
我們可以初始化一個默認的事件循環。 然後我們可以把所有的回調虛函數都用下面的方式去實現
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是個空函數,忽略。_on_completed函數回在事件循環的時候觸發,保證在主線程中調用。然後我們在這個函數再去調用js的函數。
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回調函數的map中找到對應的js函數進行調用。
這些js函數都是在事先註冊好的,就在nodejs中。
trader.on("connect",function(result){
console.log("on connected");
trader.reqUserLogin('','','',function(result,iRequestID){
console.log('login return val is '+result);
});
});
js代碼寫起來就舒服多了。