skynet 线程

启动
创建一个socket、timer、monitor线程和n个工作线程。工作线程的个数由启动时配置的参数决定。

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);
    }
    //创建监控、timer、socket线程
    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, };
    //创建n个工作线程
    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);
}


工作线程工作流程:

static void *
thread_worker(void *p) {
    struct worker_parm *wp = p;
    int id = wp->id;
    int weight = wp->weight;
    struct monitor *m = wp->m;
    struct skynet_monitor *sm = m->m[id];
    skynet_initthread(THREAD_WORKER);
    struct message_queue * q = NULL;
    while (!m->quit) {
        q = skynet_context_message_dispatch(sm, q, weight);
        if (q == NULL) {
            if (pthread_mutex_lock(&m->mutex) == 0) {
                ++ m->sleep;
                // "spurious wakeup" is harmless,
                // because skynet_context_message_dispatch() can be call at any time.
                if (!m->quit)
                    pthread_cond_wait(&m->cond, &m->mutex);
                -- m->sleep;
                if (pthread_mutex_unlock(&m->mutex)) {
                    fprintf(stderr, "unlock mutex error");
                    exit(1);
                }
            }
        }
    }
    return NULL;
}

struct message_queue * 
skynet_context_message_dispatch(struct skynet_monitor *sm, struct message_queue *q, int weight) {
    ......
    for (i=0;i<n;i++) {
        if (skynet_mq_pop(q,&msg)) {
            skynet_context_release(ctx);
            return skynet_globalmq_pop();
        } else if (i==0 && weight >= 0) {
            n = skynet_mq_length(q);
            n >>= weight;
        }
        ......
    }
    ......
}


weight 用与决定每个工作线程每次从服务的次级消息队列中取出消息的个数。

伪代码:

while(true)
    //主消息队列为空
    if main_msg_queue:is_empty()
        wait
    end
    //每次从主消息队列中取出一个次级消息队列
    ms_queue = main_msg_queue:pop()
    //然后依据线程权重参数从次级消息队列中取出一定数量的消息
    for(n)
        message = ms_queue:pop
        //调用服务的callback进行处理
        handle:callback(message)
    end
    //处理完如果还有消息队列部位空,插到主消息队列的尾部
    if not ms_queue:is_empty()
        main_msg_queue:push(ms_queue)
    end
end

工作线程:每次从主消息队列中取出一个次级消息队列。然后依据线程权重参数从次级消息队列中取出一定数量的消息。调用服务的callback进行处理。  处理完如果次级消息队列不为空,则插到主消息队列的尾部。线程继续重复上述操作直到住消息队列为空。


监控线程:

struct skynet_monitor {
    int version;
    int check_version;
    uint32_t source;
    uint32_t destination;
};
static void *
thread_monitor(void *p) {
    struct monitor * m = p;
    int i;
    int n = m->count;
    skynet_initthread(THREAD_MONITOR);
    for (;;) {
        CHECK_ABORT
        for (i=0;i<n;i++) {
            skynet_monitor_check(m->m[i]);
        }
        for (i=0;i<5;i++) {
            CHECK_ABORT
            sleep(1);
        }
    }

    return NULL;
}
//监控线程检测是否陷入死循环
void 
skynet_monitor_check(struct skynet_monitor *sm) {
    if (sm->version == sm->check_version) {
        if (sm->destination) {
            skynet_context_endless(sm->destination);
            skynet_error(NULL, "A message from [ :%08x ] to [ :%08x ] maybe in an endless loop (version = %d)", sm->source , sm->destination, sm->version);
        }
    } else {
        sm->check_version = sm->version;
    }
}
//工作线程记录消息执行记录
skynet_monitor_trigger(struct skynet_monitor *sm, uint32_t source, uint32_t destination) {
    sm->source = source;
    sm->destination = destination;
    ATOM_INC(&sm->version);
}
//调用消息回调前
skynet_monitor_trigger(sm, msg.source , handle);
//调用消息回调后
skynet_monitor_trigger(sm, 0,0);


每个工作线程对应一个skynet_monitor结构,当工作线程处理消息前用skynet_monitor记录消息的来源和目标。处理完清除。监控线程每隔五秒检测。

如何判断是否陷入死循环?
假设 version = 0,check_version = 0. 工作线程处理消息前 在skynet_monitor_trigger中sm->version = sm->version + 1,五秒后监控线程执行sm->check_version = sm->version; version = 1,check_version = 1.五秒后,监控线程再次检测,如果在五秒内工作线程执行完消息那么工作线程会执行skynet_monitor_trigger sm->version = sm->version + 1 .version = 2,check_version = 1.没执行完则version = 1,check_version = 1,sm->destination != 0.则认为消息的执行超过了五秒,可能陷入死循环。


timer线程:时间更新,定时检测,只要有线程sleep 就唤醒,向服务的消息队列中压入消息。

socket:线程后边讲解

当timer线程和socket向服务压入了消息 会调用wakeup唤醒工作线程。因为此时可能所有的工作线程都在睡眠状态。如果是服务见发送消息,不用唤醒,因为此时一定至少有一个工作线程在工作(服务代码可以执行依赖于线程的执行)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章