match_system
基于thrift,實作目標是模擬一個匹配系統,類似于游戲中兩個用戶的匹配
接收的引數:
- 一個
User變數,代表一個用戶 - 一個
string變數,可選添加的額外資訊,設定成string型別,也是為了便于之后的維護,這樣是支持以后傳入一個json表的,所以拓展性較好
version 1.0
初步實作添加用戶資訊和洗掉用戶資訊兩個操作的傳遞,這一部分很簡單,略講
c++代碼部分
// This autogenerated skeleton file illustrates how to build a server.
// You should copy it to another filename to avoid overwriting it.
#include "match_server/Match.h"
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/server/TSimpleServer.h>
#include <thrift/transport/TServerSocket.h>
#include <thrift/transport/TBufferTransports.h>
#include <iostream>
using namespace ::apache::thrift;
using namespace ::apache::thrift::protocol;
using namespace ::apache::thrift::transport;
using namespace ::apache::thrift::server;
using namespace ::match_service;
class MatchHandler : virtual public MatchIf {
public:
MatchHandler() {
// Your initialization goes here
}
int32_t add_user(const User& user, const std::string& info) {
// Your implementation goes here
printf("add_user\n");
return 0;
}
int32_t remove_user(const User& user, const std::string& info) {
// Your implementation goes here
printf("remove_user\n");
return 0;
}
};
int main(int argc, char **argv) {
int port = 9090;
::std::shared_ptr<MatchHandler> handler(new MatchHandler());
::std::shared_ptr<TProcessor> processor(new MatchProcessor(handler));
::std::shared_ptr<TServerTransport> serverTransport(new TServerSocket(port));
::std::shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());
::std::shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());
std::cout<<"Start match server ..."<<std::endl;
TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory);
server.serve();
return 0;
}
這樣當我們從match_cilent端呼叫相關函式時,便可以遠程呼叫match_server端的相關函式,進行用戶的添加和洗掉,并把用戶的資訊傳遞過去
version 2.0
使用多執行緒實作對client發送來的指令進行處理,并且進行對用戶的簡單匹配,這一部分很重要,將詳細展開講
c++代碼部分
// This autogenerated skeleton file illustrates how to build a server.
// You should copy it to another filename to avoid overwriting it.
#include "match_server/Match.h"
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/server/TSimpleServer.h>
#include <thrift/transport/TServerSocket.h>
#include <thrift/transport/TBufferTransports.h>
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <vector>
using namespace ::apache::thrift;
using namespace ::apache::thrift::protocol;
using namespace ::apache::thrift::transport;
using namespace ::apache::thrift::server;
using namespace ::match_service;
using namespace std;
class Task{
public:
User user;
string type;
private:
};
class MessageQueue{
public:
queue<Task> q;
mutex m;
condition_variable cv;
private:
}message_queue;
class Pool{
public:
void save_result(int a, int b){
printf("Match result: %d %d \n",a,b);
}
void match(){
while(users.size()>1){
auto a=users[0],b=users[1];
users.erase(users.begin());
users.erase(users.begin());
save_result(a.id, b.id);
}
}
void add(User user){
users.push_back(user);
}
void remove(User user){
for(uint32_t i = 0;i < users.size();i++)
if(user.id = users[i].id ){
users.erase(users.begin() + i);
break;
}
}
private:
vector<User> users;
}pool;
class MatchHandler : virtual public MatchIf {
public:
MatchHandler() {
// Your initialization goes here
}
int32_t add_user(const User& user, const std::string& info) {
// Your implementation goes here
printf("add_user\n");
unique_lock<mutex> lck(message_queue.m);
message_queue.q.push({user, "add"});
message_queue.cv.notify_all();
return 0;
}
int32_t remove_user(const User& user, const std::string& info) {
// Your implementation goes here
printf("remove_user\n");
unique_lock<mutex> lck(message_queue.m);
message_queue.q.push({user, "remove"});
message_queue.cv.notify_all();
return 0;
}
};
void consume_task(){
while(true){
unique_lock<mutex> lck(message_queue.m);
if(message_queue.q.empty()){
message_queue.cv.wait(lck);
}else{
auto task=message_queue.q.front();
message_queue.q.pop();
lck.unlock();
if(task.type == "add")
pool.add(task.user);
else if(task.type == "remove")
pool.remove(task.user);
pool.match();
}
}
}
int main(int argc, char **argv) {
int port = 9090;
::std::shared_ptr<MatchHandler> handler(new MatchHandler());
::std::shared_ptr<TProcessor> processor(new MatchProcessor(handler));
::std::shared_ptr<TServerTransport> serverTransport(new TServerSocket(port));
::std::shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());
::std::shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());
std::cout<<"Start match server ..."<<std::endl;
thread matching_thread(consume_task);
TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory);
server.serve();
return 0;
}
消費者:
consume_task()
生產者:
MatchHandler中的add_user()MatchHandler中的remove_user()
資源:
message_queue
我們對于client端發來的請求一共有兩個步驟:
- 接收并存盤請求
- 處理請求
所以有了以上這套生產-消費者系統
由于我們對用戶池的操作,只有add_user()和remove_user()這兩個操作,而這兩個操作已經屬于以上那一套生產-消費者模型,所以就不需要額外構建一套新的模型
接著,我們來講講代碼上的實作
首先解釋一下幾個相關函式的大致意思(因為本人也沒有完全系統學習相關c++檔案,所以寫一些個人理解,如有錯誤,歡迎各位讀者大佬指出)
unique_lock<mutex> lck(message_queue.m):這個lck變數代表一個互斥鎖,創建之后,所有用message_queue.m初始化得到鎖的地方,只能有一處能夠獲得此鎖,也就是只有一個地方能夠繼續往下執行,直到獲得到的鎖被釋放,因此對于每個需要讀取或者修改message_queue的地方,都需要加上此鎖,也就是前面的所有生產者和消費者:MatchHandler中的add_user()MatchHandler中的remove_user()consume_task()
message_queue.cv.wait(lck)和message_queue.cv.notify_all():如果訊息佇列message_queue為空,那么我們在consume_task中,出于對效率的考慮,如果一直待在consume_task的while回圈里,就會大量占用不必要的資源,這種情況有個專有名詞叫做忙等,所以我們可以暫時把這個執行緒掛起,也就是“現在沒有我要的資源,那我先休息,等有我需要的資源再叫(喚醒)我”的意思,從作業系統方面來解釋,執行了message_queue.cv.wait(lck)操作后,會把此執行緒加入到一個等待佇列上,待條件滿足了再執行message_queue.cv.notify_all()喚醒它,因為這里我們只有一個執行緒可能會發生這種忙等現象,所有我們使message_queue.cv.notify_all()和message_queue.cv.notify_one()都是等效的,都是將consume_task()中的while那一段喚醒
在consume_task()中,else分支里我們取出下一個可以進行處理的task后,就可以將lck鎖解鎖掉,因為我們已經取出了隊頭task,不在需要訪問資源,此時別的執行緒就可以開始訪問資源,所以將lck.unlock()放在message_queue.q.pop()之后,而不是pool.match()之后,只是出于對效率的優化,兩者的結果其實是一樣的
Pool的相關操作比較簡單,不再贅述了
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/304815.html
標籤:其他
上一篇:匯入Excel資料到資料庫
下一篇:突發!LayUI 宣布下線。。。
