boost context上下文切换

本文深入探讨Boost.Context库中协程的概念与应用,通过fiber和call/cc两种模式演示上下文切换,实现高效线程管理。文章提供代码示例,展示如何在单一线程内高效调度多个routine,包括经典生产者消费者模式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上下文简介

首先要理解boost::context的概念和应用场景。程序在执行的时候,当前的执行的代码环境和所处的状态,就是context。boost::context保留了当前程序执行时的寄存器等的状态,可以认为是一个上下文A;然后线程就可以去执行其他的代码,完成执行后,可以切回上下文A,并从当初切走的地方执行,而且当初的上下文的现场不变,这就是完成了一次真正意义上的上下文切换。

上线文切换,在协程和用户态线程等有重要的意义(统称它们为routine),我们可以启动一定数量的操作系统的线程,然后让routine在OS的thread中切换,切换时仅需要保留有关的上下文即可,通过一定的调度方式,是的OS的Thread不停的执行这些routine。routine的切换速度,远远快于thread,可极大提高效率。

boost::context提供了上下文的抽象,并给了两种方式,fibercall/cc的方式保留和执行上下文切换。分别介绍两种模式。

fiber的方式切换上下文

fiber是指,在fiber之后的代码片段,比如:

boost::context::fiber f{
	// your code
}

其中your code就是fiber的内容,是一个特殊格式的函数。当执行上下文时,会进行有关的调用。

int main() {
    namespace ctx = boost::context;
    int genNum{0};
    bool stop{false};
    // 初始化fiber,sink是启动该上下文的调用者的fiber,该实例中是指main的上下文
    ctx::fiber generator{[&genNum, &stop](ctx::fiber &&sink) {  
        while (true) {
            if (stop) {
                break;
            }
            genNum = (genNum + 1) * 2;
            sink = std::move(sink).resume(); // 保留当前routine的状态,切回到调用者的上下文状态,resume表示执行
        }
        return std::move(sink); // 必须返回调用者的上下文状态
    }};
    genNum = 1;
    for (int i = 0; i < 10; ++i) {
        generator = std::move(generator).resume(); // 切换到generator的上下文并执行
        std::cout << genNum << " ";
    }
    std::cout << std::endl;
    return 0;
}

编译:

g++ main.cpp -o main -g -std=c++17 -lpthread -lboost_fiber -lboost_context && ./main

最终输出:

4 10 22 46 94 190 382 766 1534 3070

注意几个关键点:

  • routine的调用,是在一个线程中的,因此同一个线程中的routine,不可以出现任何线程锁
  • fiber只有移动语义,没有构造语义
  • resume()resume_with()函数只能右值调用,因此需要使用std::move把左值转换成右值。使用右值,是为了表示恢复fiber之后,原来的fiber就无效了。

给出一个更加经典的生产者和消费者的例子,代码示例:

void pro_con(int N) {
    namespace ctx = boost::context;
    std::queue<int> buffer;
    bool stop = false; // 消除IDE报警用
    // 生产者
    ctx::fiber producer{[&buffer, &stop](ctx::fiber &&sink) {
        int cargo = 0;
        for (;;) {
            if (stop) {
                break;
            }
            buffer.push(cargo);
            std::cout << "producer push " << cargo << std::endl;
            ++cargo;
            sink = std::move(sink).resume(); // 移交控制权
        }
        return std::move(sink);
    }};

    // 消费者
    ctx::fiber consumer{[&buffer, &stop](ctx::fiber &&sink) {
        for (;;) {
            if (stop) {
                break;
            }
            int n = buffer.front();
            buffer.pop();
            std::cout << "consumer pop " << n << std::endl;
            sink = std::move(sink).resume(); // 移交控制权
        }
        return std::move(sink);
    }};

    for (int i = 0; i < N; ++i) {
        producer = std::move(producer).resume();
        consumer = std::move(consumer).resume();
    }
}

int main() {
    pro_con(10);
    return 0;
}

resume_with可以在resume之前,添加一个新的上下文,比如上面消费者的代码改成:

    // 消费者
    ctx::fiber consumer{[&buffer, &stop](ctx::fiber &&sink) {
        for (;;) {
            if (stop) {
                break;
            }
            int n = buffer.front();
            buffer.pop();
            std::cout << "consumer pop " << n << std::endl;
            sink = std::move(sink).resume_with([](ctx::fiber &&f) {
                std::cout << "====" << std::endl;
                return std::move(f);
            }); // 移交控制权
        }
        return std::move(sink);
    }};

这样,会输出如下的形式:

producer push 0
consumer pop 0
====
producer push 1
consumer pop 1
====

callcc方式切换上下文

该方式和fiber的调用基本一致,这里给出代码实例即可:


int main() {
    namespace ctx=boost::context;
    int a;
    bool stop = false;
    ctx::continuation source = ctx::callcc([&a, &stop](ctx::continuation &&sink) {
        a = 0;
        int b = 1;
        for (;;) {
            if (stop) {
                break;
            }
            sink = sink.resume();
            int next = a + b;
            a = b;
            b = next;
        }
        return std::move(sink);
    });
    for (int j = 0; j < 10; ++j) {
        std::cout << a << " ";
        source = source.resume();
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值