协程(coroutine)
1. 概述
本章节将系统性地介绍 GS 语言中的协程(Coroutine) 机制。协程是 GS 中用于实现并发、并行和协作式多任务的基本单元。本章内容面向已经学习过域、函数指针概念的 GS 语言初学者或希望了解或回顾 GS 协程基础的同学。通过本章学习,您将掌握 GS 协程的核心概念、创建方法、调度规则以及实际应用技巧。
2. 协程基础
2.1 协程的概念
在GS语言中,协程(Coroutine) 是并发编程的基本执行单元,它代表了一个可以暂停和恢复执行的轻量级任务流。与传统编程语言中的线程或简单的协程不同,GS协程融合了"域(Domain)"的概念,形成了一个独特而强大的并发编程范式。
GS中每个协程都是一个独立的执行流,拥有自己的执行上下文,但比传统操作系统线程更加轻量,可以创建成千上万个协程而不会导致系统资源耗尽。协程之间的切换通常由GS运行时系统管理,而不是操作系统内核,这使得上下文切换的成本更低。GS中的协程一般都需要在调度器的安排下,在有限的线程中排队执行。为了尽可能减少协程切换时的上下文切换开销,在正常执行流程中,GS的调度器不会主动打断协程执行,而是要等待协程主动让出(例如遇到锁、执行 coroutine.sleep 等)。
GS 不同域的协程是可以并行执行,GS运行时会尽可能地将它们调度到不同的操作系统线程上,充分利用多核处理器的计算能力。这种"域内串行、域间并行"的模型既保证了数据访问的安全性,又提供了真正的并行计算能力。
2.2 协程的创建
协程可以通过coroutin.create、coroutine.create_with_domain或coroutine.create_in_thread三种函数创建,除了指定协程名称与协程的入口函数以外,这几个协程创建函数的区别在在于是否能指定协程的执行域以及是否能指定协程在固定的线程执行。
协程可以通过coroutine.create系列的3个函数创建;除了必须指定的协程名称(可以是匿名)和入口函数相同以外,这几个函数的区别在于可以指定协程的执行域和指定调度这个协程的OS线程。接下来分别用样例介绍:
coroutine create(mixed name, string|function name_or_func, ...)此方法创建的协程执行域会被自动指定,相应规则如下:
-
创建协程时,使用入口函数的参数域做为协程执行域。
-
若入口函数的参数域为
nil时,使用创建时的当前域做为协程的执行域 -
使用默认的OS线程调度
// test.gs
#pragma parallel // 带有此选项的object下所有函数均为 parallel 的
public void test()
{
// 故意指定函数的参数域
function fn1 = (: co_entry, 1:);
fn1.bind_arg_domain(domain.create());
// 无参数域
function fn2 = (: co_entry, 2:);
// 创建协程
coroutine co1 = coroutine.create(nil, fn1);
coroutine co2 = coroutine.create(nil, fn2);
// 协程执行域就是函数的参数域
printf("----------------------\n");
printf("init domain :%M\n", this_domain());
printf("co1 entry domain: %M\n", co1.get_entry_domain());
printf("fn1 arg domain: %M\n", fn1.get_arg_domain());
printf("co2 entry domain: %M\n", co2.get_entry_domain());
printf("fn2 arg domain: %M\n", fn2.get_arg_domain());
printf("----------------------\n");
}
void co_entry(int n)
{
printf("+++++++++++++++++++++++++++\n");
printf("coroutine %d, entry domain: %M\n", n, this_domain());
printf("+++++++++++++++++++++++++++\n");
}
test();
示例2-1:coroutine.create 创建协程示例
输出结果如下:
----------------------
init domain :domain[2048:v0]zero
co1 entry domain: domain[2883348:v0]~d2883348:v0@(test@/test/test.gs)
fn1 arg domain: domain[2883348:v0]~d2883348:v0@(test@/test/test.gs)
co2 entry domain: domain[2048:v0]zero
fn2 arg domain: nil
----------------------
+++++++++++++++++++++++++++
coroutine 1, entry domain: domain[2883348:v0]~d2883348:v0@(test@/test/test.gs)
+++++++++++++++++++++++++++
Welcome driver shell.
GS 1.35.250803 Copyright (C) G-bits
Shell> +++++++++++++++++++++++++++
coroutine 2, entry domain: domain[2048:v0]zero
+++++++++++++++++++++++++++
从输出结果可以看到,由绑定了参数域的函数指针f1创建的co1