[C++ rewriting the bottom layer of Skynet 01] skynet actual combat—sunnet basic framework, create, start, wait for thread exit methods, imitate skynet to write message class

[C++ Rewriting Skynet Bottom Layer 01] Skynet Practical Combat-sunnet basic framework, create, open, wait for thread exit methods, imitate skynet to write message classes
[C++ Rewrite Skynet Bottom Layer 02] Skynet Practical Combat-Imitating Skynet to write service classes, object management under multi-threading, spin lock mutex program writing, hash table management objects, and summary of program running steps
[C++ Rewriting Skynet Bottom Layer 03] Skynet Practical Combat-Insertion and pop-up of global message queue, imitating Skynet to send messages, message transmission and message processing between services
[C++ Rewriting Skynet Bottom Layer 04] Skynet Practical Combat – Demonstration program PingPong, waiting and waking up of worker threads, improved scheduling function

Article directory

  • Preface
  • 1. Build the basic framework of Sunnet
  • 2. How to enable multi-core and multi-threading in C++
    • 2.1. Create thread object
    • 2.2 Simulate skynet opening thread
    • 2.3 Wait for the thread to exit
  • 3. Actor system (imitation of skynet writing message class)
    • 3.1 Message class of Actor model
    • 3.2 Message base class
    • 3.3 BaseMsg subclass ServiceMsg
  • Summarize

Foreword

The bottom layer of skynet is written in C language, which provides a set of Actor model mechanisms. This series will use C++ to imitate skynet as the main line to learn operating system concepts such as “threads”, “locks”, and “condition variables”. Implement Sunnet scheduling function
The running environment of this series is a Linux environment. Please configure the relevant environment (g++, cmake, etc.) in advance. For specific configuration, please refer to a blog I wrote before:
[Linux c++ practical] Teach you step by step how to build a c++ project from scratch on Linux, and introduce Cmake in detail (C++|Cmake|Ubuntu)

The learning reference book for this series “Millions of Games: Large-Scale Game Server Development”

In this section, we will first build the basic framework of Sunnet, and use C++ to create threads, start threads, wait for threads to exit, and imitate skynet to write message classes.

1. Build the basic framework of Sunnet

First create an object, which is globally unique and can represent the entire system. An Actor system contains multiple services, and Sunnet::inst manages them, so in the first step we will implement the Sunnet::inst code first

//Sunnet.h file
class Sunnet {<!-- -->
public:
    //single case
    static Sunnet* inst; //Using singleton mode, the static variable inst is declared and assigned a value in the constructor
    public:
    //Constructor
    Sunnet();
    //Initialize and start
    void Start();
};
//Sunnet.cpp
#include <iostream>
#include "Sunnet.h"
using namespace std;

//single case
Sunnet* Sunnet::inst;
Sunnet::Sunnet(){<!-- -->
    inst = this;
}
//Start the system
void Sunnet::Start() {<!-- -->
    cout << "Hello Sunnet" << endl;
}

//main.cpp
#include "Sunnet.h"
int main(){<!-- -->
  new Sunnet();
  Sunnet::inst->Start();
  //Some logic after turning on the system
  return 0;
}

After building, compiling, and executing, the program can clearly see that “Hello Sunnet” is printed. Although it is a simple program here, the Sunnet framework has been set up, and development will be carried out on this basis.

2. How to enable multi-core and multi-threading in C++

2.1. Create thread objects

There are at least four methods of creating threads in C++ (For the introduction of the four creation methods, please refer to my summary), here we choose the “thread object” method. In Worker.h, we define the worker class, which will equip each thread with a worker object. The thread object has two attributes: id and eachnum for subsequent In the service scheduling function.

//Worker.h
#pragma once
#include <thread>
#include "Sunnet.h"
classSunnet;

using namespace std;

class Worker {<!-- -->
public:
    int id; //number
    int eachNum; //How many messages are processed each time
    void operator()(); //Thread function
};

operator()() is a thread execution method. It is an infinite loop. Each loop will print out the workingid and then block for 0.1s.

//worker.cpp
#include <iostream>
#include <unistd.h>
#include "Worker.h"
using namespace std;
//thread function
//void operator()() means overloading the bracket operator, which is the core content of the thread object.
void Worker::operator()() {<!-- -->
    while(true) {<!-- -->
        cout << "working id:" <<id <<endl;
        usleep(100000) //0.1s
    }
}

2.2 Simulate skynet opening thread

You only need the following two lines of code to create a thread, and then the thread will start running and printing continuously. The specific application code will be introduced later:

 Worker* worker = new Worker(); // Create a worker object
 thread* wt = new thread(*worker); //Create a thread and specify its corresponding worker object

In order to allow Sunnet to manage threads, threads, and thread objects are all managed by Sunnet::inst, so member functions and member variables are added to Sunnet, among which:
workerThreads represents threads, and Sunnet::inst will automatically schedule three of them.
Each thread must carry a thread object (worker), so workers is also an array, storing 3 workers.

//Sunnet.h
#pragma once
#include <vector>
#include "Worker.h"
#include <unordered_map>
using namespace std;
class Worker;
class Sunnet {<!-- -->
public:
    //single case
    static Sunnet* inst;
public:
    //Constructor
    Sunnet();
    //Initialize and start
    void Start();
    //wait to run
    void Wait(); //New
//Add new
private:
    //worker thread
    int WORKER_NUM = 3; //Number of working threads (configuration), you can set it yourself
    vector<Worker*> workers; //worker object
    vector<thread*> workerThreads; //Thread, a thread array (vector type)
private:
    //Start the working thread
    void StartWorker();
};

Then go to the cpp file and write the corresponding function code StartWorker(), and Wait():

In StartWorker(), a worker object is first created each time, and its attributes are assigned values, and then the thread wt is created, and both wt and the thread object worker are added to the array managed by Sunnet.

//Sunnet.cpp
//StartWorker(),Wait() definition
//Start the worker thread
void Sunnet::StartWorker() {<!-- -->
    for (int i = 0; i < WORKER_NUM; i + + ) {<!-- -->
        cout << "start worker thread:" << i << endl;
        //Create thread object
        Worker* worker = new Worker();
        worker->id = i;
        worker->eachNum = 2 << i;
        //Create thread
        thread* wt = new thread(*worker);
        //Add to list
        workers.push_back(worker);
        workerThreads.push_back(wt);
    }
}
//wait
void Sunnet::Wait() {<!-- -->
    if( workerThreads[0]) {<!-- -->
        workerThreads[0]->join();
    }
}

Sunnet::inst manages workerThreads and workers, and finally opens the system’s start method and calls StartWorker() in it.

//Sunnet.cpp
//Start the system
void Sunnet::Start() {<!-- -->
    cout << "Hello Sunnet" << endl;
    //Open Worker
    StartWorker(); //New
}

The results after compiling and running are as follows:

2.3 Wait for the thread to exit

In 2.2 we defined the wait() function. The “join” method of the thread in the code can block the caller until the thread exits. If wait (main function) is called in the main thread, the main thread will wait for the first worker. thread (workerThreads[0]) until this thread exits, but the worker thread is in an infinite loop, excluding the outside. Therefore, the main thread will be permanently blocked.

//main.cpp
int main(){<!-- -->
  new Sunnet();
  Sunnet::inst->Start();
  Sunnet::inst->Wait(); //New
  return 0;
}

After running the code, each thread will continue to print the workingid, and since multiple threads operate at the same time, conflicts will inevitably occur (the content of the next thread will be printed before the previous thread has finished printing). Methods to resolve thread conflicts will be discussed later. .

3. Actor system (imitation of skynet writing message class)

3.1 Message class of Actor model

Actors need at least two objects: “service” and “message”. In addition to the global management of Sunnet::list mentioned in the previous section, the Actor system also needs to include service objects (threads) and message objects (Msg, communication between messages).
We first design the message class structure for Sunnet. The base class BaseMsg stores the general properties of the message. ServiceMsg is used for message transfer between services, and others are used for Socekt communication.











































ServiceMsg









BaseMsg









xxxMsg









xxxMsg







3.2 Message base class

//Basic content of message base class (Msg.h):

//Msg.h
//Basic content of message base class:
#pragma once
#include <memory>
using namespace std;

//Message base class
class BaseMsg {<!-- -->
public:
    enum TYPE {<!-- --> //Message type
        SERVICE = 1,
    };
    uint8_t type; //Message type
    char load[999999]{<!-- -->}; //Used to detect memory leaks
    virtual ~BaseMsg(){<!-- -->};
};

3.3 BaseMsg subclass ServiceMsg

source represents the sender, recording the sender’s service ID, buff refers to the message content, which is a smart pointer pointing to a char type array, and size represents the message length;

//Msg.h
//Message between services
class ServiceMsg : public BaseMsg {<!-- -->
public:
    uint32_t source; //Message sender
    shared_ptr<char> buff; //Smart pointer, message content
    size_t size; //Message content size
};


Summary

In this article, we first build the basic framework of Sunnet, then write C++ methods to create threads, start threads, wait for threads to exit, and finally imitate skynet to write message classes. In subsequent studies, we will continue to write content related to queues and locks, imitating skynet. Write service classes, and introduce the use of spin locks, object management under multi-threading, how to make full use of the CPU, etc.

To be continued…