【skynet】服务调度实验

写在前面

名词解释

skynet 是一个多线程框架,功能按服务划分,正确区分各个名词十分重要。

  • 线程:程序可以并行运行多少个执行序,一般指 worker 线程
  • 服务:模块运行时的实例,多个服务可以并发执行
  • 协程:调度最小单元,一个服务由多个协程组成

skynet 线程

  • skynet 关于线程的代码如下,一共有5种线程。

    /* skynet_imp.h */
    #define THREAD_WORKER 0
    #define THREAD_MAIN 1
    #define THREAD_SOCKET 2
    #define THREAD_TIMER 3
    #define THREAD_MONITOR 4
    
  • 按启动顺序排序是:

    1. thread_mian (主线程)
    2. thread_monitor (监控线程)
    3. thread_timer (定时器线程)
    4. thread_socket (网络线程)
    5. thread_worker (工作线程)
  • 配置里面的 thread 指的是 thread_worker 数量。

    /* skynet_start.c */
    static void
    start(int thread) {
        pthread_t pid[thread+3];
    
        struct monitor *m = skynet_malloc(sizeof(*m));
        memset(m, 0, sizeof(*m));
        m->count = thread;
        m->sleep = 0;
    
        m->m = skynet_malloc(thread * sizeof(struct skynet_monitor *));
        int i;
        for (i=0;i<thread;i++) {
            m->m[i] = skynet_monitor_new();
        }
        if (pthread_mutex_init(&m->mutex, NULL)) {
            fprintf(stderr, "Init mutex error");
            exit(1);
        }
        if (pthread_cond_init(&m->cond, NULL)) {
            fprintf(stderr, "Init cond error");
            exit(1);
        }
    
        create_thread(&pid[0], thread_monitor, m);
        create_thread(&pid[1], thread_timer, m);
        create_thread(&pid[2], thread_socket, m);
    
        static int weight[] = { 
            -1, -1, -1, -1, 0, 0, 0, 0,
            1, 1, 1, 1, 1, 1, 1, 1, 
            2, 2, 2, 2, 2, 2, 2, 2, 
            3, 3, 3, 3, 3, 3, 3, 3, };
        struct worker_parm wp[thread];
        for (i=0;i<thread;i++) {
            wp[i].m = m;
            wp[i].id = i;
            if (i < sizeof(weight)/sizeof(weight[0])) {
                wp[i].weight= weight[i];
            } else {
                wp[i].weight = 0;
            }
            create_thread(&pid[i+3], thread_worker, &wp[i]);
        }
    
        for (i=0;i<thread+3;i++) {
            pthread_join(pid[i], NULL); 
        }
    
        free_monitor(m);
    }
    


准备工作

首先要有一个编译好,而且工作正常的 skynet 。最好已经对服务间通信有一定了解。

代码演示

基本示例

我们将启用两个服务,一个 hello 服务,每秒打印一次 Hello, World! 。另一个是 main 服务,每秒打印一次 This is main.

  • 主服务

    -- main.lua
    local skynet = require "skynet"
    
    skynet.init(function()
        -- 启动 hello
        skynet.newservice("hello")
    end)
    
    skynet.start(function()
        skynet.fork(function()
            while true do
                -- 每秒打印一次
                skynet.error("This is main.")
                skynet.sleep(100)
            end
        end)
    end)
    
  • hello 服务

    -- hello.lua
    local skynet = require "skynet"
    
    skynet.start(function()
        skynet.fork(function()
            while true do
                -- 每秒打印一次
                skynet.error("Hello, World!")
                skynet.sleep(100)
            end
        end)
    end)
    
  • 运行结果

    [:00000002] LAUNCH snlua bootstrap
    [:00000003] LAUNCH snlua launcher
    [:00000004] LAUNCH snlua cdummy
    [:00000005] LAUNCH harbor 0 4
    [:00000006] LAUNCH snlua datacenterd
    [:00000007] LAUNCH snlua service_mgr
    [:00000008] LAUNCH snlua main
    [:00000009] LAUNCH snlua hello
    [:00000009] Hello, World!
    [:00000008] This is main.
    [:00000002] KILL self
    [:00000009] Hello, World!
    [:00000008] This is main.
    [:00000009] Hello, World!
    [:00000008] This is main.
    ...
    

    结果为 main 服务和 hello 服务每秒打印一次。

示例变体一

  • 将 hello 服务改成死循环

    修改代码,验证服务间的独立性。

    • main 服务不变
    • hello 服务加入死循环代码
    -- hello.lua
    local skynet = require "skynet"
    
    skynet.start(function()
        skynet.fork(function()
            while true do
                -- 每秒打印一次
                skynet.error("Hello, World!")
                skynet.sleep(100)
                -- 进入死循环
                while true do
                end
            end
        end)
    end)
    
  • 运行结果

    [:00000002] LAUNCH snlua bootstrap
    [:00000003] LAUNCH snlua launcher
    [:00000004] LAUNCH snlua cdummy
    [:00000005] LAUNCH harbor 0 4
    [:00000006] LAUNCH snlua datacenterd
    [:00000007] LAUNCH snlua service_mgr
    [:00000008] LAUNCH snlua main
    [:00000009] LAUNCH snlua hello
    [:00000009] Hello, World!
    [:00000008] This is main.
    [:00000002] KILL self
    [:00000008] This is main.
    [:00000008] This is main.
    [:00000008] This is main.
    [:00000008] This is main.
    [:00000008] This is main.
    [:00000008] This is main.
    [:00000008] This is main.
    [:00000008] This is main.
    [:00000008] This is main.
    [:00000008] This is main.
    [:00000000] A message from [ :00000000 ] to [ :00000009 ] maybe in an endless loop (version = 85)
    [:00000008] This is main.
    ...
    

    结果为

    • hello 服务只打印了一次
    • main 服务正常执行,每秒打印一次
    • monitor 还识别出来 hello 服务死循环了

示例变体二

  • 启动两个 hello 服务,工作线程数改为 2

    • hello 服务保持死循环不变
    • 修改 main 服务,启动两个 hello 服务
    • 修改配置,改成两个线程
    -- main.lua
    local skynet = require "skynet"
    
    skynet.init(function()
        -- 启动 2 个 hello
        skynet.newservice("hello")
        skynet.newservice("hello")
    end)
    
    skynet.start(function()
        skynet.fork(function()
            while true do
                -- 每秒打印一次
                skynet.error("This is main.")
                skynet.sleep(100)
            end
        end)
    end)
    
  • 运行结果

    [:00000002] LAUNCH snlua bootstrap
    [:00000003] LAUNCH snlua launcher
    [:00000004] LAUNCH snlua cdummy
    [:00000005] LAUNCH harbor 0 4
    [:00000006] LAUNCH snlua datacenterd
    [:00000007] LAUNCH snlua service_mgr
    [:00000008] LAUNCH snlua main_ping
    [:00000009] LAUNCH snlua hello
    [:00000009] Hello, World!
    [:0000000a] LAUNCH snlua hello
    [:0000000a] Hello, World!
    [:00000008] This is main.
    [:00000002] KILL self
    

    结果为

    • hello 服务只打印了一次
    • main 服务只打印了一次
    • 少了一条“ maybe in an endless loop ”打印
  • 为什么会少了一条 monitor 打印呢

    因为 monitor 的打印是通过 logger 服务输出的。当所有 worker 线程进入了死循环, logger 便无法输出内容了。

结论

  • 同一个模块的多个服务实例,可以占用多个线程,享有平等执行权。
  • 其他服务不能抢占当前服务,只能等当前服务让出执行权。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值