V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
monkeyNik
V2EX  ›  分享创造

开源 C 语言库 Melon: I/O 线程模型

  •  
  •   monkeyNik ·
    Water-Melon · 2023-01-25 22:56:37 +08:00 · 1011 次点击
    这是一个创建于 703 天前的主题,其中的信息可能已经有所发展或是发生改变。

    本文展示开源 C 语言库 Melon 中的 I/O 线程模型。在 Melon 中存在三种线程模型,本篇仅对 I/O 线程模型进行说明。

    关于 Melon 库,这是一个开源的 C 语言库,它具有:开箱即用、无第三方依赖、安装部署简单、中英文文档齐全等优势。

    Github repo

    简介

    首先简单介绍一些什么是 I/O 线程模型。这一话题将引入图形界面或移动 APP 开发进行说明。

    在常规的涉及网络通信的图形界面客户端程序(如微信、QQ 等等)中,客户端既要负责界面展示,也要负责网络数据传输。然而网络传输操作实际上是会存在拥塞和丢包等不稳定因素,因此就存在了阻塞通信与非阻塞通信。在图形界面的程序开发中,很多图形接口的使用与非阻塞 I/O 很难融合于一个线程内。因此,如果一个线程即负责图形渲染,又负责 IO 通信,则会出现界面卡顿甚至卡死。

    很显然,这样的人机交互界面很不友好,因此就出现了渲染与 IO 分离,分为了两个线程。即主线程做界面渲染,子线程做 IO 通讯。这也就是 I/O 线程这一名词的由来。

    Melon 支持 I/O 线程模型不仅仅是为了图形界面开发的需要,也是为了兼顾一些中间件的开发需要。下面我们来一起看一下其使用。

    使用

    我们先给出代码,再进行说明。

    #include "mln_iothread.h"
    #include <string.h>
    #include <stdio.h>
    #include <errno.h>
    
    static void msg_handler(mln_iothread_t *t, mln_iothread_ep_type_t from, mln_iothread_msg_t *msg)
    {
        mln_u32_t type = mln_iothread_msg_type(msg);
        printf("msg type: %u\n", type);
    }
    
    static void *entry(void *args)
    {
        int n;
        mln_iothread_t *t = (mln_iothread_t *)args;
        while (1) {
            n = mln_iothread_recv(t, user_thread);
            printf("recv %d message(s)\n", n);
        }
    
        return NULL;
    }
    
    int main(void)
    {
        int i, rc;
        mln_iothread_t t;
        struct mln_iothread_attr tattr;
    
        tattr.nthread = 1;
        tattr.entry = (mln_iothread_entry_t)entry;
        tattr.args = &t;
        tattr.handler = (mln_iothread_msg_process_t)msg_handler;
        if (mln_iothread_init(&t, &tattr) < 0) {
            fprintf(stderr, "iothread init failed\n");
            return -1;
        }
        for (i = 0; i < 1000000; ++i) {
            if ((rc = mln_iothread_send(&t, i, NULL, io_thread, 1)) < 0) {
                fprintf(stderr, "send failed\n");
                return -1;
            } else if (rc > 0)
                continue;
        }
        sleep(1);
        mln_iothread_destroy(&t);
        sleep(3);
        printf("DONE\n");
    
        return 0;
    }
    

    main中的流程大致如下:

    1. 初始化 I/O 线程
    2. 对 I/O 线程发送 100 万次消息
    3. 休眠 1 秒钟
    4. 销毁 I/O 线程
    5. 休眠三秒钟
    6. 输出完成并退出

    这里对 I/O 线程初始化说明一下:

    struct mln_iothread_attr {
        mln_u32_t                   nthread; //拉起几个 I/O 线程
        mln_iothread_entry_t        entry; //I/O 线程入口函数
        void                       *args; //I/O 线程入口参数
        mln_iothread_msg_process_t  handler; //消息处理函数
    };
    

    消息处理函数handler是当主线程或 I/O 线程调用mln_iothread_recv接收到消息后被调用,用于让用户自定义对消息的处理。

    本例中,I/O 线程死循环调用mln_iothread_recv接收从主线程发来的消息,然后在该函数中调用回调msg_handler来处理主线程发来的消息。

    同时,可以看到初始化属性结构中包含一个名为nthread的属性,表示 I/O 线程可以有多个。而 Melon 中的事件分发模型(mln_event.h)是支持多线程的,因此利用 I/O 线程模型可以实现:基于同一个事件分发模型,多线程处理事件分发模型上的事件的能力。

    除此之外,mln_iothread_send还支持了阻塞发送和非阻塞发送。阻塞发送表示,只有对端线程接收了消息,这个函数才会返回,而非阻塞则是发送后不等待对方接收立即返回。

    结语

    I/O 模型现在在 Melang 脚本语言启动器中被使用,用于使 Melang 脚本可以启动多个线程来处理脚本程序,有点类似 Go 中协程在线程间漂移。

    欢迎各位对 Melon 感兴趣的读者访问其Github 仓库

    感谢阅读!

    vitovan
        1
    vitovan  
       2023-01-29 19:06:36 +08:00
    一个人做的吗?感觉工作量很大的样子。
    monkeyNik
        2
    monkeyNik  
    OP
       2023-01-29 19:21:37 +08:00   ❤️ 1
    @vitovan 是的呀,慢慢积累呀
    vitovan
        3
    vitovan  
       2023-01-30 11:32:25 +08:00
    @monkeyNik #2 厉害!
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2348 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 16:06 · PVG 00:06 · LAX 08:06 · JFK 11:06
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.