Make writing a habit together! This is the fourth day of my participation in the “Gold Digging Day New Plan · April More text Challenge”. Click here for more details.

Continue to improve our matching system

Perfect match_client

Directly on the code:

.# 1. Read input from the terminal
from sys import stdin


def operate(op, user_id, username, score) :
    transport = TSocket.TSocket('localhost'.9090)
    transport = TTransport.TBufferedTransport(transport)
    protocol = TBinaryProtocol.TBinaryProtocol(transport)
    client = Match.Client(protocol)
    transport.open(a)# 2. Remote invocation
    user = User(user_id, username, score)
    if op == "add":
        client.add_user(user, "")
    elif op == "remove":
        client.remove_user(user, "")

    transport.close()


def main() :
  	# 3. Read input separated by Spaces
    for line in stdin:
        op, user_id, username, score = line.split("")
        operate(op, int(user_id), username, int(score))


if __name__ == "__main__":
    main()

Copy the code

Read input, remote call:

With a few simple modifications to the code that thrift helped us generate, our client was almost done.

Perfect match_server

There are several points about the matching mechanism:

  • Define a message queue to process incoming messages from clientsadd_userremove_userThe command
  • Define a user pool and add users to the same user pool for matching
  • Add and remove cannot be done at the same time, must have a lock
  • Classic producer-consumer problem

To import the necessary headers, then define the structure of the message queue:

// match_server/main.cpp.#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <vector>
#include <unistd.h>.struct Task {
  User user;
  string type;
};

struct MsgQueue {
  queue<Task> q;
  mutex m;
  condition_variable cv;
}msg_queue;
Copy the code

Handle queues, consumers, pay attention to the use of locks, block threads when there is no message, wait to wake up:

void consume_task(a) {
  while(true) {
    unique_lock<mutex> lck(msg_queue.m);
    if (msg_queue.q.empty()) {
      msg_queue.cv.wait(lck);
    } else {
      auto task = msg_queue.q.front(a); msg_queue.q.pop(a);// Don't forget to learn about locks
      lck.unlock(a);// Add or delete the user pool
      if (task.type == "add") pool.add(task.user);
      else if (task.type == "remove") pool.remove(task.user);
			
      // A match is made in the user pool after each message is processed
      pool.match(a); }}}int main(int argc, char **argv) {... cout <<"Start the match server 😎" << endl;
  // open a separate thread to the consumer
  thread matching_thread(consume_task);

  server.serve(a);return 0;
}

Copy the code

Add and remove user methods, only one method can operate on the queue at a time, and then wake up the consuming thread:

class MatchHandler : virtual public MatchIf {
  public:
    MatchHandler() {}int32_t add_user(const User& user, const std::string& info) {
      printf("add_user\n");
			
      unique_lock<mutex> lck(msg_queue.m);
      msg_queue.q.push({user, "add"});
      msg_queue.cv.notify_all(a);return 0;
    }

    int32_t remove_user(const User& user, const std::string& info) {
      printf("remove_user\n");

      unique_lock<mutex> lck(msg_queue.m);
      msg_queue.q.push({user, "remove"});
      msg_queue.cv.notify_all(a);return 0; }};Copy the code

Create a new user pool for matching. Here the simplest matching method is taken, as long as there are two users in the pool to match (later can be optimized) :

class Pool {
  public:
    void save_result(int a, int b) {
      // Prints the matching result
      printf("Match Result: %d %d\n", a, b);
    }

    void match(a) {
      while (users.size(a) >1) {
        // Two people match
        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(a); i++){if (users[i].id == user.id) {
          users.erase(users.begin()+i);
          break; }}}private:
  	/ / user pool
  	vector<User> users;
}pool;
Copy the code

Compile to run, you can see that it has run:

Let’s stop here, you can check github for the specific code, there are a lot of follow-up optimization methods, I’m here to throw out a piece of advice:

  • The gap
  • time
  • multithreading
  • .