600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > boost context上下文切换

boost context上下文切换

时间:2023-05-10 11:33:02

相关推荐

boost context上下文切换

上下文简介

首先要理解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 0consumer pop 0====producer push 1consumer 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;}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。