前言
要理解C++20的协程怎么用,得先理解关键字,三个关键字:co_await、co_yield、co_return,所有函数体内有这三个关键字其中一个或以上的,都将被转换成协程函数。还有两个概念:awaiter、generator,awaiter是co_await作用的对象,而generator是管理协程句柄(std::coroutine_handle)的对象,也是协程函数的返回值。
另外,C++20的协程是缺少“调度器”的,需要自行实现协程的调度
注意点:下面我会说“协程”和“协程函数”,注意两者区别
C++20协程关键字
co_return [result]
result是协程的最终返回结果,没有result时即无返回
co_yield value
co_yield会将协程挂起,value是协程挂起时返回的值,不可省略,且必须与co_return同类型,当co_return无返回值时,不可使用co_yield,每次执行到co_yield都可以获取一次协程的返回值(即多次返回值)
co_await value
①value是一个“awaiter”对象,不可省略。
②co_await可重载,也是一个“运算符函数”,重载的co_await运算符函数必须返回一个awaiter。
awaiter说明
awaiter是co_await直接作用的对象,它决定了co_await操作时协程如何响应
awaiter类型必须拥有以下三个接口函数:
await_ready
co_await接收到awaiter参数时首先执行的函数,返回值bool。当返回值为false时,协程将被挂起,然后执行await_suspend函数;当返回值为true时,协程不会挂起而继续执行,将跳过await_suspend函数。
await_suspend
await_suspend返回类型有三种:
①bool,如果返回false,则立即恢复协程而不挂起。如果返回true,协程挂起。
②void,视为返回true
③std::coroutine_handle<>,挂起当前协程,并立即恢复返回的另一个协程,就是该重要的机制,支撑起协程的嵌套和转移
关于await_suspend的实参——协程的句柄,可以是默认的std::coroutine_handle<>,也可以是指定的std::coroutine_handle<promise_type>,视功能需求自定义。
await_resume
await_resume函数的返回值就是co_await运算符的返回值。当协程在co_await挂起后被恢复时,或者协程在await_ready返回true时,将会调用await_resume。
其中,标准库提供了两个内置的awaiter,分别是std::suspend_always和std::suspend_never
promise_type说明
promise_type是协程内部状态的控制核心,控制着协程的创建、挂起、恢复和销毁,它还负责协程的最终返回(或异常)以及协程返回值的传递。
promise_type类型的接口函数如下表:
| 函数描述 | 函数说明 |
|---|---|
| generator get_return_object() | 在协程执行前调用,用于构造generator对象,然后将存放了promise_type引用的协程句柄传送到generator对象中存放。该接口的返回值就是协程函数的直接返回值 |
| awaiter initial_suspend() | 在协程初始化时调用,返回一个awaiter对象 |
| awaiter final_suspend() noexcept(true) | 在协程结束时调用。必须有noexcept修饰 |
| awaiter yield_value(type value) | co_yield操作时调用,value即是co_yield的参数。【无co_yield时非必须】 |
| void unhandled_exception() | 协程发生异常时调用 |
| void return_void() | 当co_return执行且无返回值时调用。有该函数的时候不能存在return_value函数。【co_return有返回值时非必须】 |
| void return_value(type value) | 当co_return执行且有返回值时调用,value即是co_return的参数。有该函数的时候不能存在return_void函数。【co_return无返回值时非必须】 |
| static generator get_return_object_on_allocation_failure() | 当标识为noexcept的内存分配函数返回nullptr时,协程函数在返回generator时将会通过调用此接口获得返回值。【非必须】 |
generator说明
generator是管理协程句柄(std::coroutine_handle)的对象,也是协程函数的返回值,一般由generator对象来操作promise_type对象
generator的对象由promise_type的get_return_object函数生成并返回
协程库实现
理解了上面的各个要点后,就能着手实现一个简单的协程库了。
以下就是源码,支持协程嵌套和转移,也支持异步执行及完成后唤醒,但是线程池、事件队列需要自行实现
#ifndef CO_TASK_H
#define CO_TASK_H
#include <coroutine>
#include <functional> // std::function
#include <memory> // std::shared_ptr std::make_shared
// 用于在协程结束时自动恢复续体的 Awaitable
template<typename _Tp>
struct FinalAwaitable
{
bool await_ready() const noexcept
{ return false; }
// 对 C++20 对话式对称传输的支持:返回 handle 会立即切换到该协程
std::coroutine_handle<> await_suspend(std::coroutine_handle<_Tp> h) noexcept
{
// 这里的 h 是当前结束的协程(子协程)
if constexpr (std::is_same<_Tp, void>::value)
{ return std::noop_coroutine(); }
else
{
auto& promise = h.promise();
if (promise.continuation)
{ return promise.continuation; }
return std::noop_coroutine();
}
}
void await_resume() noexcept
{}
};
template<typename _Ret, typename _ThreadPoolEnqueue, typename _EventLoopEnqueue>
struct ThreadPoolAwaiter
{
_ThreadPoolEnqueue pool_enqueue;
_EventLoopEnqueue loop_enqueue;
std::function<_Ret()> func;
_Ret result;
ThreadPoolAwaiter(_ThreadPoolEnqueue &&p, _EventLoopEnqueue &&l, std::function<_Ret()> &&f)
: pool_enqueue(std::move(p))
, loop_enqueue(std::move(l))
, func(std::move(f))
{}
// 永远挂起,以便将任务切走
bool await_ready() const noexcept
{ return false; }
void await_suspend(std::coroutine_handle<> handle)
{
// 1. 将任务提交给线程池
pool_enqueue([this, handle]() mutable {
try
{
if constexpr (!std::is_void_v<_Ret>)
{
result = func();
}
else
{
func();
}
}
catch (...)
{
// 异常处理逻辑可以根据需求扩展
}
// 2. 线程池任务完成后,将“恢复协程”的操作投递回主事件循环
loop_enqueue([handle]() {
handle.resume();
});
});
}
_Ret await_resume()
{ return std::move(result); }
};
template<typename _Tp>
struct Task
{
public:
struct promise_type;
using co_handle = std::coroutine_handle<promise_type>;
using value_type = _Tp;
using reference = _Tp &;
using rvalue_reference = _Tp &&;
using const_reference = const _Tp &;
struct promise_type
{
value_type value;
std::exception_ptr exception;
std::coroutine_handle<> continuation; // 父协程的句柄
Task get_return_object()
{ return Task(co_handle::from_promise(*this)); }
std::suspend_always initial_suspend()
{ return {}; }
FinalAwaitable<promise_type> final_suspend() noexcept
{ return {}; }
void return_value(rvalue_reference v)
{ value = std::move(v); }
void return_value(const_reference v)
{ value = v; }
std::suspend_always yield_value(rvalue_reference v)
{
value = std::move(v);
return {};
}
std::suspend_always yield_value(const_reference v)
{
value = v;
return {};
}
void unhandled_exception()
{ exception = std::current_exception(); }
};
public:
Task(co_handle h)
: _M_handle(new co_handle(h), _S_on_release)
{ }
// 是否已执行完毕
operator bool() const noexcept
{ return _M_handle && *_M_handle && _M_handle->done(); }
void operator()()
{
if(_M_handle && *_M_handle)
{ _M_handle->resume(); }
}
// 获取返回值
reference operator*() noexcept
{ return _M_handle->promise().value; }
auto operator co_await() noexcept
{
struct Awaiter
{
co_handle callee;
bool await_ready() const noexcept
{ return !callee || callee.done(); }
// h 是当前正在 co_await 的父协程句柄
std::coroutine_handle<> await_suspend(std::coroutine_handle<> h) noexcept
{
callee.promise().continuation = h; // 绑定续体
return callee; // 对称传输:切换到子协程执行
}
value_type await_resume()
{
if (callee.promise().exception)
{ std::rethrow_exception(callee.promise().exception); }
return std::move(callee.promise().value);
}
};
return Awaiter{*_M_handle};
}
private:
static void _S_on_release(co_handle *h)
{
h->destroy();
delete h;
}
private:
std::shared_ptr<co_handle> _M_handle;
};
// 针对 void 的特化
template<>
struct Task<void>
{
public:
struct promise_type;
using co_handle = std::coroutine_handle<promise_type>;
struct promise_type
{
std::exception_ptr exception;
std::coroutine_handle<> continuation; // 父协程的句柄
Task get_return_object()
{ return Task(co_handle::from_promise(*this)); }
std::suspend_always initial_suspend()
{ return {}; }
FinalAwaitable<promise_type> final_suspend() noexcept
{ return {}; }
void return_void()
{}
std::suspend_always yield_void()
{ return {}; }
void unhandled_exception()
{ exception = std::current_exception(); }
};
Task(co_handle h)
: _M_handle(new co_handle(h), _S_on_release)
{ }
// 是否已执行完毕
operator bool() const noexcept
{ return _M_handle && *_M_handle && _M_handle->done(); }
void operator()()
{
if(_M_handle && *_M_handle)
{ _M_handle->resume(); }
}
// 获取返回值
void operator*() const noexcept
{ }
auto operator co_await() noexcept
{
struct Awaiter
{
co_handle callee;
bool await_ready() const noexcept
{ return !callee || callee.done(); }
// h 是当前正在 co_await 的父协程句柄
std::coroutine_handle<> await_suspend(std::coroutine_handle<> h) noexcept
{
callee.promise().continuation = h; // 绑定续体
return callee; // 对称传输:切换到子协程执行
}
void await_resume()
{
if (callee.promise().exception)
{ std::rethrow_exception(callee.promise().exception); }
}
};
return Awaiter{*_M_handle};
}
private:
static void _S_on_release(co_handle *h)
{
h->destroy();
delete h;
}
private:
std::shared_ptr<co_handle> _M_handle;
};
/**
* 在线程池pool中异步执行一个函数,得到结果后在loop中唤醒协程
* @param pool 线程池的提交函数,接收一个 std::function<void()> 参数
* @param loop 事件循环的提交函数,接收一个 std::function<void()> 参数
* @param f 需要在线程池中异步执行的函数,返回值将被协程获取,在loop中继续执行co_await返回后的操作
*/
template<typename _ThreadPoolEnqueue, typename _EventLoopEnqueue, typename _Func>
auto AsyncExec(_ThreadPoolEnqueue &&pool, _EventLoopEnqueue &&loop, _Func &&f)
{
using Ret = std::invoke_result_t<_Func>;
return ThreadPoolAwaiter<Ret, _ThreadPoolEnqueue, _EventLoopEnqueue>(std::move(pool), std::move(loop), std::move(f));
}
#endif // CO_TASK_H
使用例子
1.事件队列的实现
文件名:event_queue.h
#ifndef EVENT_QUEUE_H
#define EVENT_QUEUE_H
#include <atomic> // std::atomic_flag
#include <queue> // std::queue
#include <functional> // std::function
#include <thread> // std::this_thread::yield
template<typename _Tp>
class EventQueue
{
public:
using EventWrapper = std::function<_Tp()>;
public:
void Push(EventWrapper &&e)
{
_M_Lock();
_M_event_queue.push(std::move(e));
_M_Unlock();
}
EventWrapper Pop()
{
EventWrapper result;
_M_Lock();
if(_M_event_queue.empty())
{
_M_Unlock();
return result;
}
EventWrapper e = std::move(_M_event_queue.front());
_M_event_queue.pop();
_M_Unlock();
result.swap(e);
return result;
}
private:
void _M_Lock() noexcept
{
while(_M_lock.test_and_set())
{ std::this_thread::yield(); }
}
void _M_Unlock() noexcept
{ _M_lock.clear(); }
private:
std::atomic_flag _M_lock;
std::queue<EventWrapper> _M_event_queue;
};
#endif // EVENT_QUEUE_H
2.事件循环的实现
文件名:event_loop.h
#ifndef EVENT_LOOP_H
#define EVENT_LOOP_H
/**
* 事件循环类
*/
#include "event_queue.h"
#include <thread> // std::this_thread::sleep_for
#include <chrono> // std::chrono::milliseconds
#include <functional> // std::function
#include <future> // std::future std::future_status
#include <atomic> // std::atomic_bool
class EventLoop
{
public:
using EventCallback = std::function<void()>;
template<std::size_t _EmptySleep = 10, std::size_t _NormalSleep = 1>
void Run()
{
_M_flag.store(true, std::memory_order_release);
while(_M_flag.load(std::memory_order_acquire))
{
typename EventQueue<void>::EventWrapper f = _M_event_queue.Pop();
if(!f)
{
std::this_thread::sleep_for(std::chrono::milliseconds(_EmptySleep));
continue;
}
f();
std::this_thread::sleep_for(std::chrono::milliseconds(_NormalSleep));
}
}
void Enqueue(EventCallback &&e)
{ _M_event_queue.Push(std::move(e)); }
void Stop() noexcept
{ _M_flag.store(false, std::memory_order_release); }
private:
std::atomic_bool _M_flag;
EventQueue<void> _M_event_queue;
};
#endif // EVENT_LOOP_H
3.线程池的实现
#ifndef THREAD_POOL_H
#define THREAD_POOL_H
#include <future> // std::future
#include <atomic> // std::atomic_flag std::atomic_bool
#include <queue> // std::queue
#include <functional> // std::function
#include <thread> // std::this_thread::sleep_for std::this_thread::yield
#include <vector> // std::vector
#include <memory> // std::shared_ptr std::make_shared
namespace detail
{
template<typename _AtomicFlag = std::atomic_flag>
class SpinLock
{
public:
SpinLock()
: _M_lock(ATOMIC_FLAG_INIT)
{}
void lock() noexcept
{
while(_M_lock.test_and_set())
{ std::this_thread::yield(); }
}
void unlock() noexcept
{ _M_lock.clear(std::memory_order_release); }
private:
std::atomic_flag _M_lock;
};
} // namespace detail
template<typename _Lock = detail::SpinLock<>>
class ThreadPool
{
private:
using AsyncTaskWrapper = std::function<void()>;
public:
ThreadPool(std::size_t num_threads = 1)
{
_M_flag.store(true, std::memory_order_release);
while(num_threads-- > 0)
{
_M_threads.emplace_back(std::async(std::launch::async, [this](){
_M_MainLoop();
}));
}
}
~ThreadPool()
{
Stop();
for(auto &t : _M_threads)
{ t.get(); }
}
// 异步执行,不关心结果
void Async(std::function<void()> &&async_task)
{
_M_Lock();
_M_async_task_queue.push(std::move(async_task));
_M_Unlock();
}
void Stop() noexcept
{ _M_flag.store(false, std::memory_order_release); }
private:
void _M_MainLoop()
{
while(_M_flag.load(std::memory_order_acquire))
{
_M_Lock();
if(_M_async_task_queue.empty())
{
_M_Unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
continue;
}
AsyncTaskWrapper task = std::move(_M_async_task_queue.front());
_M_async_task_queue.pop();
_M_Unlock();
task();
}
}
void _M_Lock() noexcept
{ _M_lock.lock(); }
void _M_Unlock() noexcept
{ _M_lock.unlock(); }
private:
std::queue<AsyncTaskWrapper> _M_async_task_queue;
_Lock _M_lock;
std::atomic_bool _M_flag;
std::vector<std::future<void>> _M_threads;
};
#endif // THREAD_POOL_H
4.测试案例
①多线程下的测试案例
#ifdef _WIN32
#include <cstdlib> // system
#endif
#include <iostream> // std::cout
#include <format> // std::format
#include "thread_pool.h"
#include "event_loop.h"
#include "co_task.h"
static EventLoop Main;
static ThreadPool<> Pool(2);
// 一个模拟耗时任务
int HeavyComputation(int input)
{
std::cout << "[HeavyComputation] caling thread_ID: " << std::this_thread::get_id() << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2));
return input * 2;
}
// 协程函数3
Task<double> MyCoroutine3(int id)
{
std::cout << std::format("[Event Loop---{}---] start coroutine 3 thread_ID: ", id) << std::this_thread::get_id() << std::endl;
co_yield 1.5;
std::cout << std::format("[Event Loop---{}---] coroutine 3 status 1 thread_ID: ", id) << std::this_thread::get_id() << std::endl;
co_yield 2.5;
std::cout << std::format("[Event Loop---{}---] coroutine 3 status 2 thread_ID: ", id) << std::this_thread::get_id() << std::endl;
co_yield 3.5;
std::cout << std::format("[Event Loop---{}---] coroutine 3 status 3 thread_ID: ", id) << std::this_thread::get_id() << std::endl;
co_yield 4.5;
std::cout << std::format("[Event Loop---{}---] coroutine 3 status 4 thread_ID: ", id) << std::this_thread::get_id() << std::endl;
co_return 5.5;
}
// 协程函数2
Task<int> MyCoroutine2(int id)
{
std::cout << std::format("[Event Loop---{}---] start coroutine 2 thread_ID: ", id) << std::this_thread::get_id() << std::endl;
auto t3 = MyCoroutine3(id);
double sum = 0;
while(!t3)
{
double value = co_await AsyncExec(
std::bind(&ThreadPool<>::Async, &Pool, std::placeholders::_1),
std::bind(&EventLoop::Enqueue, &Main, std::placeholders::_1),
[t3]() mutable {
t3();
return *t3;
}
);
sum += value;
}
std::cout << std::format("[Event Loop---{}---] coroutine 3 result: {}", id, sum) << std::endl;
int result = co_await AsyncExec(
std::bind(&ThreadPool<>::Async, &Pool, std::placeholders::_1),
std::bind(&EventLoop::Enqueue, &Main, std::placeholders::_1),
[]() {
return HeavyComputation(20);
}
);
std::cout << std::format("[Event Loop---{}---] get coroutine 2 result: {}", id, result) << std::endl;
co_return result;
}
// 协程函数
Task<void> MyCoroutine(int id)
{
std::cout << std::format("[Event Loop---{}---] start coroutine 1 thread_ID: ", id) << std::this_thread::get_id() << std::endl;
int ret = co_await MyCoroutine2(id);
std::cout << std::format("[Event Loop---{}---] get coroutine 2 result: {}", id, ret) << std::endl;
int result = co_await AsyncExec(
std::bind(&ThreadPool<>::Async, &Pool, std::placeholders::_1),
std::bind(&EventLoop::Enqueue, &Main, std::placeholders::_1),
[]() {
return HeavyComputation(10);
}
);
std::cout << std::format("[Event Loop---{}---] get coroutine 1 result: {} thread_ID: ", id, result) << std::this_thread::get_id() << std::endl;
Main.Stop(); // 结束后停止循环
}
void func()
{
// 多个多层级的协程
Task<void> t1 = MyCoroutine(1);
Task<void> t2 = MyCoroutine(2);
Main.Enqueue([&t1](){
t1();
});
Main.Enqueue([&t2](){
t2();
});
// 运行主循环
std::cout << "main loop start" << std::endl;
Main.Run();
}
int main()
{
func();
#ifdef _WIN32
system("pause");
#endif
return 0;
}
输出结果(输出打印没加锁会看着有点乱):
main loop start
[Event Loop---1---] start coroutine 1 thread_ID: 1
[Event Loop---1---] start coroutine 2 thread_ID: 1
[Event Loop---1---] start coroutine 3 thread_ID: 2
[Event Loop---2---] start coroutine 1 thread_ID: 1
[Event Loop---2---] start coroutine 2 thread_ID: 1
[Event Loop---2---] start coroutine 3 thread_ID: 3
[Event Loop---1---] coroutine 3 status 1 thread_ID: 3
[Event Loop---2---] coroutine 3 status 1 thread_ID: 2
[Event Loop---1---] coroutine 3 status 2 thread_ID: 2
[Event Loop---2---] coroutine 3 status 2 thread_ID: 2
[Event Loop---1---] coroutine 3 status 3 thread_ID: 2
[Event Loop---2---] coroutine 3 status 3 thread_ID: 2
[Event Loop---1---] coroutine 3 status 4 thread_ID: 2
[Event Loop---2---] coroutine 3 status 4 thread_ID: [Event Loop---1---] coroutine 3 result: 17.5
3
[HeavyComputation] caling thread_ID: 3
[Event Loop---2---] coroutine 3 result: 17.5
[HeavyComputation] caling thread_ID: 2
[Event Loop---1---] get coroutine 2 result: 40
[Event Loop---1---] get coroutine 2 result: 40
[HeavyComputation] caling thread_ID: [Event Loop---2---] get coroutine 2 result: 40
2
[Event Loop---2---] get coroutine 2 result: 40
[HeavyComputation] caling thread_ID: 3
[Event Loop---1---] get coroutine 1 result: 20 thread_ID: 1
②单线程下事件循环的测试案例
#ifdef _WIN32
#include <cstdlib> // system
#endif
#include <iostream> // std::cout
#include <format> // std::format
#include "event_loop.h"
#include "co_task.h"
static EventLoop Main;
// 一个模拟耗时任务
int HeavyComputation(int input)
{
std::cout << "[HeavyComputation] caling thread_ID: " << std::this_thread::get_id() << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2));
return input * 2;
}
// 协程函数3
Task<double> MyCoroutine3(int id)
{
std::cout << std::format("[Event Loop---{}---] start coroutine 3 thread_ID: ", id) << std::this_thread::get_id() << std::endl;
co_yield 1.5;
std::cout << std::format("[Event Loop---{}---] coroutine 3 status 1 thread_ID: ", id) << std::this_thread::get_id() << std::endl;
co_yield 2.5;
std::cout << std::format("[Event Loop---{}---] coroutine 3 status 2 thread_ID: ", id) << std::this_thread::get_id() << std::endl;
co_yield 3.5;
std::cout << std::format("[Event Loop---{}---] coroutine 3 status 3 thread_ID: ", id) << std::this_thread::get_id() << std::endl;
co_yield 4.5;
std::cout << std::format("[Event Loop---{}---] coroutine 3 status 4 thread_ID: ", id) << std::this_thread::get_id() << std::endl;
co_return 5.5;
}
// 协程函数2
Task<int> MyCoroutine2(int id)
{
std::cout << std::format("[Event Loop---{}---] start coroutine 2 thread_ID: ", id) << std::this_thread::get_id() << std::endl;
auto t3 = MyCoroutine3(id);
double sum = 0;
while(!t3)
{
double value = co_await AsyncExec(
std::bind(&EventLoop::Enqueue, &Main, std::placeholders::_1),
std::bind(&EventLoop::Enqueue, &Main, std::placeholders::_1),
[t3]() mutable {
t3();
return *t3;
}
);
sum += value;
}
std::cout << std::format("[Event Loop---{}---] coroutine 3 result: {}", id, sum) << std::endl;
int result = co_await AsyncExec(
std::bind(&EventLoop::Enqueue, &Main, std::placeholders::_1),
std::bind(&EventLoop::Enqueue, &Main, std::placeholders::_1),
[]() {
return HeavyComputation(20);
}
);
std::cout << std::format("[Event Loop---{}---] get coroutine 2 result: {}", id, result) << std::endl;
co_return result;
}
// 协程函数
Task<void> MyCoroutine(int id)
{
std::cout << std::format("[Event Loop---{}---] start coroutine 1 thread_ID: ", id) << std::this_thread::get_id() << std::endl;
int ret = co_await MyCoroutine2(id);
std::cout << std::format("[Event Loop---{}---] get coroutine 2 result: {}", id, ret) << std::endl;
int result = co_await AsyncExec(
std::bind(&EventLoop::Enqueue, &Main, std::placeholders::_1),
std::bind(&EventLoop::Enqueue, &Main, std::placeholders::_1),
[]() {
return HeavyComputation(10);
}
);
std::cout << std::format("[Event Loop---{}---] get coroutine 1 result: {} thread_ID: ", id, result) << std::this_thread::get_id() << std::endl;
Main.Stop(); // 结束后停止循环
}
void func()
{
// 多个多层级的协程
Task<void> t1 = MyCoroutine(1);
Task<void> t2 = MyCoroutine(2);
Main.Enqueue([&t1](){
t1();
});
Main.Enqueue([&t2](){
t2();
});
// 运行主循环
std::cout << "main loop start" << std::endl;
Main.Run();
}
int main()
{
func();
#ifdef _WIN32
system("pause");
#endif
return 0;
}
输出结果:
main loop start
[Event Loop---1---] start coroutine 1 thread_ID: 1
[Event Loop---1---] start coroutine 2 thread_ID: 1
[Event Loop---2---] start coroutine 1 thread_ID: 1
[Event Loop---2---] start coroutine 2 thread_ID: 1
[Event Loop---1---] start coroutine 3 thread_ID: 1
[Event Loop---2---] start coroutine 3 thread_ID: 1
[Event Loop---1---] coroutine 3 status 1 thread_ID: 1
[Event Loop---2---] coroutine 3 status 1 thread_ID: 1
[Event Loop---1---] coroutine 3 status 2 thread_ID: 1
[Event Loop---2---] coroutine 3 status 2 thread_ID: 1
[Event Loop---1---] coroutine 3 status 3 thread_ID: 1
[Event Loop---2---] coroutine 3 status 3 thread_ID: 1
[Event Loop---1---] coroutine 3 status 4 thread_ID: 1
[Event Loop---2---] coroutine 3 status 4 thread_ID: 1
[Event Loop---1---] coroutine 3 result: 17.5
[Event Loop---2---] coroutine 3 result: 17.5
[Thread Pool] caling thread_ID: 1
[Thread Pool] caling thread_ID: 1
[Event Loop---1---] get coroutine 2 result: 40
[Event Loop---1---] get coroutine 2 result: 40
[Event Loop---2---] get coroutine 2 result: 40
[Event Loop---2---] get coroutine 2 result: 40
[Thread Pool] caling thread_ID: 1
[Thread Pool] caling thread_ID: 1
[Event Loop---1---] get coroutine 1 result: 20 thread_ID: 1