跟我学C++中级篇——Linux常见的异步编程方式

一、异步编程

异步编程是相对同步编程而说的,所谓异步就是程序交互双方互不干扰各自工作,然后通过某种机制通知工作的状态变化。比如消息机制、事件机制等。异步编程是一种编程范式,一般与非阻塞的工作方式相关,实现的主要方式就是多线程与回调协作完成。
异步编程主要应用于一些耗时和多任务的场景,常见的如网络编程和UI的响应等。特别是在高并发的网络服务程序中,应用更是广泛。

二、主要的异步编程模型

从整体上看,不同的平台对异步编程的支持可能有所不同,但基本上,大多数的异步编程模型都可以划分以下几类:
1、基础的多线程+回调机制
这种架构在前面反复分析,其实它也是所有异步架构(除硬件)的最底层的主要实现。
2、事件驱动机制
事件驱动机制在前面也分析过,就是通过事件改变当前的行为和状态
3、消息驱动机制
消息驱动类似于事件驱动,是利用消息来驱动程序的变化
4、协程
这个是一个相对较新的架构,通过协程的高效性实现异步编程

在基于上述的编程模型上,又可以封装抽象出更多的常见的框架机制,比如很多语言中的async/await框架、IO事件编程模型的epoll等。而C++中的std:async其实就是多线程的机制,更高抽象的多线程中的Actor异步框架也是如此。

三、Linux中常见的异步编程方式

具体到Linux平台,常见的异步编程方式有:
1、信号机制
信号机制大家应该都比较清楚,就是通过Linux系统内定义的信号进行异步的通信
2、事件驱动机制
事件驱动机制包括多种,既有I/O的事件驱动也有文件系统的inotify文件系统的事件通知等
3、异步I/O机制
这个就有点更高层的抽象了,如AIO库中的异步I/O机制,当然也包括其它开源框架或库的相关异步I/O机制

四、例程

异步编程分析过很多,相关例程也不少,这里只给几个简单的例子:
1、信号的例程

//信号发送
#include <signal.h>
#include <stdio.h>

int main() {
    // 发送 SIGKILL 信号
    int ret = kill(1234, SIGKILL);
    if (ret != 0) {
        return -1;
    } 
    
    return 0;
}
//接收
#include <signal.h>
#include <stdio.h>
#include <unistd.h>

void recvSig(int sig) {
    printf("recv signal id: %d\n", sig);
}

int main() {
    signal(SIGINT, recvSig);
    gtechar();
    return 0;
}

2、事件的例程

#include <stdio.h>
#include <stdlib.h>
#include <sys/inotify.h>
#include <unistd.h>
#include <limits.h>

#define EVENT_SIZE  (sizeof(struct inotify_event))
#define BUF_LEN     (1024 * (EVENT_SIZE + NAME_MAX + 1))

int main(int argc, char **argv) {
    char * pathname = argv[1];
    if (argc != 2) {
        exit(EXIT_FAILURE);
    }

    int fd, wd;
    char buf[BUF_LEN];

    // init
    fd = inotify_init();
    if (fd == -1) {
        exit(EXIT_FAILURE);
    }

    wd = inotify_add_watch(fd, pathname, IN_CREATE | IN_DELETE | IN_MODIFY);
    if (wd == -1) {
        close(fd);
        exit(EXIT_FAILURE);
    }


    while (1) {
        ssize_t length = read(fd, buf, BUF_LEN);
        if (length == -1) {
            break;
        }

        // event control
        for (char *ptr = buf; ptr < buf + length; ) {
            struct inotify_event *event = (struct inotify_event *)ptr;

            if (event->len) {
                if (event->mask & IN_CREATE) {
                    if (event->mask & IN_ISDIR) {
                        printf("dir created: %s/%s\n", pathname, event->name);
                    } else {
                        printf("file created: %s/%s\n", pathname, event->name);
                    }
                }

                if (event->mask & IN_DELETE) {
                    if (event->mask & IN_ISDIR) {
                        printf("dir deleted: %s/%s\n", pathname, event->name);
                    } else {
                        printf("file deleted: %s/%s\n", pathname, event->name);
                    }
                }

                if (event->mask & IN_MODIFY) {
                    if (!(event->mask & IN_ISDIR)) {
                        printf("file modified: %s/%s\n", pathname, event->name);
                    }
                }
            }


            ptr += EVENT_SIZE + event->len;
        }
    }

    // release
    inotify_rm_watch(fd, wd);
    close(fd);

    return 0;
}

3、AIO的例子

#include <libaio.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define BUF_SIZE 4096

int main() {
    int fd;
    io_context_t ctx;
    struct iocb cb;
    struct io_event events[1];
    char *buffer=NULL;

    // support O_DIRECT
    fd = open("testfile", O_RDWR | O_CREAT | O_DIRECT, 0644);
    if (fd < 0) {
        exit(1);
    }

    memset(&ctx, 0, sizeof(ctx));
    if (io_setup(10, &ctx) < 0) {   
        exit(1);
    }

    posix_memalign((void**)&buffer, 512, BUF_SIZE);
    memset(buffer, 'A', BUF_SIZE);

    io_prep_pwrite(&cb, fd, buffer, BUF_SIZE, 0);  // move head
    cb.data = buffer;  

    // submit
    if (io_submit(ctx, 1, &cb) != 1) {
        io_destroy(ctx);
        exit(1);
    }

    // block
    int num_events = io_getevents(ctx, 1, 1, events, NULL);
    if (num_events < 0) {
        io_destroy(ctx);
        exit(1);
    }

    if (events[0].res != BUF_SIZE) {
        fprintf(stderr, "Write error: %s\n", strerror(-events[0].res));
    } else {
        printf("async write ok!\n");
    }

    // clear
    free(buffer);
    close(fd);
    io_destroy(ctx);
    return 0;
}

五、总结

编程技术中,很多技术是一条点、线、面结合的过程,往往不是一个单纯的知识点就可以搞清楚的。所以,在日常的开发实践中,多和理论知识印证,加强知识的整理和抽象,就会不断的提升自己的编程水平。不要怕总结的不到位甚至可能有一些错误,这都是成长必然的路径!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值