帮酷LOGO
  • 显示原文与译文双语对照的内容
A library of C++ coroutine abstractions.

  • 源代码名称:cppcoro
  • 源代码网址:http://www.github.com/lewissbaker/cppcoro
  • cppcoro源代码文档
  • cppcoro源代码下载
  • Git URL:
    git://www.github.com/lewissbaker/cppcoro.git
  • Git Clone代码到本地:
    git clone http://www.github.com/lewissbaker/cppcoro
  • Subversion代码到本地:
    $ svn co --depth empty http://www.github.com/lewissbaker/cppcoro
    Checked out revision 1.
    $ cd repo
    $ svn up trunk
  • CppCoro - C+ +的一个协作库

    库提供了一组通用原语,用于利用 N4680 中描述的synergy TS 。

    这些包括:

    • 协作者类型
      • task<T>
      • shared_task<T>
      • generator<T>
      • recursive_generator<T>
      • async_generator<T>
    • Awaitable类型
      • single_consumer_event
      • single_consumer_auto_reset_event
      • async_mutex
      • async_manual_reset_event
      • async_auto_reset_event
      • async_latch
    • 鍑芥暟
      • sync_wait()
      • when_all()
      • when_all_ready()
      • fmap()
      • schedule_on()
      • resume_on()
    • 取消 用户 登记
      • cancellation_token
      • cancellation_source
      • cancellation_registration
    • 调度程序和i/o
      • io_service
      • io_work_scope
      • filereadable_filewritable_file
      • read_only_filewrite_only_fileread_write_file

    库是一个实验库,它探索可以以在 C++ 协同建议上构建的高性能。可以扩展的异步编程抽象空间。

    它已经经开源,希望它的他人能够发现它是有用的,并且 C++ 社区可以以提供反馈。

    它需要一个支持 synergy TS的编译器:

    • Windows + Visual Studio 2017 Windows Build Status
    • Linux + Clang 5.0/6.0 + libc++ Build Status

    Linux版本除了 io_context 和文件 I/O 相关的类,但是尚未实现 Linux ( 有关更多信息,请参见 issue #15 ) 。

    类详细信息

    task<T>

    任务表示一个异步计算,在执行程序的执行直到任务等待时执行。

    例如:

    #include<cppcoro/read_only_file.hpp>#include<cppcoro/task.hpp>cppcoro::task<int> count_lines(std::string path)
    {
     auto file = co_await cppcoro::read_only_file::open(path);
     int lineCount = 0;
     char buffer[1024];
     size_t bytesRead;
     std::uint64_t offset = 0;
     do {
     bytesRead = co_await file.read(offset, buffer, sizeof(buffer));
     lineCount += std::count(buffer, buffer + bytesRead, 'n');
     offset += bytesRead;
     } while (bytesRead> 0);
     co_return lineCount;
    }
    cppcoro::task<> usage_example()
    {
     // Calling function creates a new task but doesn't start// executing the coroutine yet. cppcoro::task<int> countTask = count_lines("foo.txt");
     //.. .// Coroutine is only started when we later co_await the task.int lineCount = co_await countTask;
     std::cout <<"line count = " <<lineCount <<std::endl;
    }

    API概述:

    // <cppcoro/task.hpp>namespacecppcoro{
     template<typename T>
     classtask {
     public:using promise_type = <unspecified>;
     using value_type = T;
     task() noexcept;
     task(task&& other) noexcept;
     task& operator=(task&& other);
     // task is a move-only type.task(const task& other) = delete;
     task& operator=(const task& other) = delete;
     // Query if the task result is ready.boolis_ready() constnoexcept;
     // Wait for the task to complete and return the result or rethrow the// exception if the operation completed with an unhandled exception.//// If the task is not yet ready then the awaiting coroutine will be// suspended until the task completes. If the the task is_ready() then// this operation will return the result synchronously without suspending. <unspecified> operatorco_await() const & noexcept;
     <unspecified> operatorco_await() const && noexcept;
     // Returns an awaitable that can be co_await'ed to suspend the current// coroutine until the task completes.//// The 'co_await t.when_ready()' expression differs from 'co_await t' in// that when_ready() only performs synchronisation, it does not return// the result or rethrow the exception.//// This can be useful if you want to synchronise with the task without// the possibility of it throwing an exception. <unspecified> when_ready() constnoexcept;
     };
     template<typename T>
     voidswap(task<T>& a, task<T>& b);
     // Apply func() to the result of the task, returning a new task that// yields the result of 'func(co_await task)'.template<typename FUNC, typename T>
     task<std::invoke_result_t<FUNC, T&&>> fmap(FUNC func, task<T> task);
     // Call func() after task completes, returning a task containing the// result of func().template<typename FUNC>
     task<std::invoke_result_t<FUNC>> fmap(FUNC func, task<void> task);
    }

    通过调用返回 task<T>的协作器函数来创建 task<T> 对象。

    协同程序必须包含 co_await 或者 co_return的用法。 请注意,task<T> 协同程序可能不使用 co_yield 关键字。

    如果调用了返回 task<T>的程序集,则在必要时分配一个程序框架,并在协作框架中捕获参数。 is在is正文的开始处挂起,并将执行返回给调用方,表示异步计算的task<T> 值。

    task<T> 值为 co_await 时,协同程序主体将开始执行。 这将挂起等待的协同程序,并开始执行与等待的task<T> 值关联的协同程序。

    稍后将在完成等待的task<T> 协作器执行执行的线程上恢复等待协同程序。 IE 。执行 co_return 或者引发终止执行程序执行的未处理异常的线程。

    如果任务已经运行完成,然后再次等待它将获得已经计算的结果而不挂起等待的协同程序。

    如果在等待 task 值之前销毁它,那么coroutine永远不会执行并且 destructor destructs捕获的参数,释放coroutine框架使用的任何内存。

    shared_task<T>

    shared_task<T> 类是一个coroutine类型,它异步生成一个值。

    在执行任务之前,它是'迟缓',直到某些程序员等待它。

    它是'共享',可以以复制任务值,允许多个引用对要创建的任务的结果进行复制。 它还允许多个协同程序同时等待结果。

    任务将在第一个 co_await 任务的线程上开始执行。 随后的awaiters将被挂起,当任务完成时排队,或者者在任务已经经到达完成后将继续进行。

    如果在等待任务完成的同时挂起 awaiter,则它将在完成任务执行的线程上继续。 IE 。执行 co_return 或者引发终止执行程序执行的未处理异常的线程。

    API摘要

    namespacecppcoro{
     template<typename T = void>
     classshared_task {
     public:using promise_type = <unspecified>;
     using value_type = T;
     shared_task() noexcept;
     shared_task(const shared_task& other) noexcept;
     shared_task(shared_task&& other) noexcept;
     shared_task& operator=(const shared_task& other) noexcept;
     shared_task& operator=(shared_task&& other) noexcept;
     voidswap(shared_task& other) noexcept;
     // Query if the task has completed and the result is ready.boolis_ready() constnoexcept;
     // Returns an operation that when awaited will suspend the// current coroutine until the task completes and the result// is available.//// The type of the result of the 'co_await someTask' expression// is an l-value reference to the task's result value (unless T// is void in which case the expression has type 'void').// If the task completed with an unhandled exception then the// exception will be rethrown by the co_await expression. <unspecified> operatorco_await() constnoexcept;
     // Returns an operation that when awaited will suspend the// calling coroutine until the task completes and the result// is available.//// The result is not returned from the co_await expression.// This can be used to synchronise with the task without the// possibility of the co_await expression throwing an exception. <unspecified> when_ready() constnoexcept;
     };
     template<typename T>
     booloperator==(const shared_task<T>& a, const shared_task<T>& b) noexcept;
     template<typename T>
     booloperator!=(const shared_task<T>& a, const shared_task<T>& b) noexcept;
     template<typename T>
     voidswap(shared_task<T>& a, shared_task<T>& b) noexcept;
     // Wrap a task in a shared_task to allow multiple coroutines to concurrently// await the result.template<typename T>
     shared_task<T> make_shared_task(task<T> task);
     // Apply func() to the result of the task, returning a new task that// yields the result of 'func(co_await task)'.template<typename FUNC, typename T>
     task<std::invoke_result_t<FUNC, T&&>> fmap(FUNC func, shared_task<T> task);
     // Call func() after task completes, returning a task containing the// result of func().template<typename FUNC>
     task<std::invoke_result_t<FUNC>> fmap(FUNC func, shared_task<void> task);
    }

    在同一实例中,shared_task<T> 上的所有const方法都可以安全地与其他实例同时调用。 不安全地将 shared_task<T>的非常量方法调用到 shared_task<T>的同一个实例上的任何它的他方法。

    task<T>的比较

    在调用coroutine函数时,任务不会立即启动执行,因此 shared_task<T> 类与 task<T> 相似。 任务仅在首次等待时才开始执行。

    它与 task<T> 不同,因为生成的任务对象可以复制,允许多个任务对象引用同一个异步结果。 它同时支持多个协同程序,同时等待任务的结果。

    结果是结果始终是一个值引用,从而不能将结果移动到局部变量的( 由于结果可能是共享的) 值引用。 此外,由于需要维护引用计数并支持多个 awaiters,它还具有稍微高一点的运行时间开销。

    generator<T>

    表示coroutine类型that类型的值序列,其中的值以延迟和同步的方式产生值。

    协调器主体可以使用 co_yield 关键字对 T 类型的值进行 yield 。 注意,;不能使用 co_await 关键字,必须同步生成值。

    例如:

    cppcoro::generator<const std::uint64_t> fibonacci()
    {
     std::uint64_t a = 0, b = 1;
     while (true)
     {
     co_yield b;
     auto tmp = a;
     a = b;
     b += tmp;
     }
    }voidusage()
    {
     for (auto i : fibonacci())
     {
     if (i> 1'000'000) break;
     std::cout <<i <<std::endl;
     }
    }

    当返回 generator<T>的程序集函数被称为is时,初始化 is 。 当 generator<T>::begin() 方法被调用并继续,直到到达第一个 co_yield 语句或者协同程序运行完成时,协同程序才进入协调器主体。

    返回的迭代器不等于 end() 迭代器,然后取消引用迭代器将返回对传递给 co_yield 语句的值的引用。

    在迭代器上调用 operator++() 将继续执行协作器并继续直到下一个 co_yield 点到达或者coroutine运行到 completion() 为止。

    协同程序抛出的任何未处理异常都将从 begin() 或者 operator++() 调用传播到调用方。

    API摘要:

    namespacecppcoro{
     template<typename T>
     classgenerator {
     public:using promise_type = <unspecified>;
     classiterator {
     public:using iterator_category = std::input_iterator_tag;
     using value_type = std::remove_reference_t<T>;
     using reference = value_type&;
     using pointer = value_type*;
     using difference_type = std::size_t;
     iterator(const iterator& other) noexcept;
     iterator& operator=(const iterator& other) noexcept;
     // If the generator coroutine throws an unhandled exception before producing// the next element then the exception will propagate out of this call. iterator& operator++();
     reference operator*() const noexcept;
     pointer operator->() const noexcept;
     booloperator==(const iterator& other) const noexcept;
     booloperator!=(const iterator& other) const noexcept;
     };
     // Constructs to the empty sequence.generator() noexcept;
     generator(generator&& other) noexcept;
     generator& operator=(generator&& other) noexcept;
     generator(const generator& other) = delete;
     generator& operator=(const generator&) = delete;
     ~generator();
     // Starts executing the generator coroutine which runs until either a value is yielded// or the coroutine runs to completion or an unhandled exception propagates out of the// the coroutine. iterator begin();
     iterator end() noexcept;
     // Swap the contents of two generators.voidswap(generator& other) noexcept;
     };
     template<typename T>
     voidswap(generator<T>& a, generator<T>& b) noexcept;
     // Apply function, func, lazily to each element of the source generator// and yield a sequence of the results of calls to func().template<typename FUNC, typename T>
     generator<std::invoke_result_t<FUNC, T&>> fmap(FUNC func, generator<T> source);
    }

    recursive_generator<T>

    一个 recursive_generator 类似于 generator,除了它被设计为更有效地支持嵌套序列元素作为外部序列的元素。

    除了能够对类型为 T的值进行赋值之外,还可以对类型为 recursive_generator<T>的值。

    co_yield 值为 recursive_generator<T> 时,所产生发生器的所有元素都作为当前发生器的元素产生。 当消费者完成了嵌套生成器的所有元素后,当前程序员将继续执行下一个元素。

    对于迭代数据结构,recursive_generator<T> 优于 generator<T>的好处在于,iterator::operator++() 能够直接恢复叶,而不是为每个元素恢复/挂起 O(depth) 协作器,而不是恢复。 下面是额外的开销。

    例如:

    // Lists the immediate contents of a directory.cppcoro::generator<dir_entry> list_directory(std::filesystem::path path);
    cppcoro::recursive_generator<dir_entry> list_directory_recursive(std::filesystem::path path)
    {
     for (auto& entry : list_directory(path))
     {
     co_yield entry;
     if (entry.is_directory())
     {
     co_yield list_directory_recursive(entry.path());
     }
     }
    }

    注意,将 fmap() 运算符应用到 recursive_generator<T> 将会对 generator<U> 类型产生 yield,而不是对 recursive_generator<U> 。 这是因为 fmap 通常不会在递归上下文中使用,我们试图避免 recursive_generator 产生的额外开销。

    async_generator<T>

    表示类型为coroutine类型的coroutine类型,其中值是延迟生成的值,值可以异步生成,并且可以生成值。

    协作器主体可以同时使用 co_awaitco_yield 表达式。

    生成器的使用者可以使用基于循环的for co_await 范围来使用值。

    例子

    cppcoro::async_generator<int> ticker(int count, threadpool& tp)
    {
     for (int i = 0; i <count; ++i)
     {
     co_await tp.delay(std::chrono::seconds(1));
     co_yield i;
     }
    }
    cppcoro::task<> consumer(threadpool& tp)
    {
     auto sequence = ticker(tp);
     forco_await(std::uint32_t i : sequence)
     {
     std::cout <<"Tick " <<i <<std::endl;
     }
    }

    API摘要

    // <cppcoro/async_generator.hpp>namespacecppcoro{
     template<typename T>
     classasync_generator {
     public:classiterator {
     public:using iterator_tag = std::forward_iterator_tag;
     using difference_type = std::size_t;
     using value_type = std::remove_reference_t<T>;
     using reference = value_type&;
     using pointer = value_type*;
     iterator(const iterator& other) noexcept;
     iterator& operator=(const iterator& other) noexcept;
     // Resumes the generator coroutine if suspended// Returns an operation object that must be awaited to wait// for the increment operation to complete.// If the coroutine runs to completion then the iterator// will subsequently become equal to the end() iterator.// If the coroutine completes with an unhandled exception then// that exception will be rethrown from the co_await expression. <unspecified> operator++() noexcept;
     // Dereference the iterator. pointer operator->() const noexcept;
     reference operator*() const noexcept;
     booloperator==(const iterator& other) const noexcept;
     booloperator!=(const iterator& other) const noexcept;
     };
     // Construct to the empty sequence.async_generator() noexcept;
     async_generator(const async_generator&) = delete;
     async_generator(async_generator&& other) noexcept;
     ~async_generator();
     async_generator& operator=(const async_generator&) = delete;
     async_generator& operator=(async_generator&& other) noexcept;
     voidswap(async_generator& other) noexcept;
     // Starts execution of the coroutine and returns an operation object// that must be awaited to wait for the first value to become available.// The result of co_await'ing the returned object is an iterator that// can be used to advance to subsequent elements of the sequence.//// This method is not valid to be called once the coroutine has// run to completion. <unspecified> begin() noexcept;
     iterator end() noexcept;
     };
     template<typename T>
     voidswap(async_generator<T>& a, async_generator<T>& b);
     // Apply 'func' to each element of the source generator, yielding a sequence of// the results of calling 'func' on the source elements.template<typename FUNC, typename T>
     async_generator<std::invoke_result_t<FUNC, T&>> fmap(FUNC func, async_generator<T> source);
    }

    async_generator的早期终止

    async_generator 对象被分解时,它请求取消底层协同程序。 如果协同程序已经运行完成或者当前在 co_yield 表达式中挂起,则将立即销毁该协同程序。 否则,程序员将继续执行,直到运行到完成或者到达下一个 co_yield 表达式。

    当of帧被销毁时,将执行作用域中所有变量的析构函数,以确保生成器的资源被清除。

    注意,调用者必须确保不要销毁 async_generator 对象,而消费者程序员正在执行等待下一项的co_await 表达式。

    single_consumer_event

    这是一个简单的手动重置事件类型,仅支持一次等待它的单个协同程序。 这可以用于

    API摘要:

    // <cppcoro/single_consumer_event.hpp>namespacecppcoro{
     classsingle_consumer_event {
     public:single_consumer_event(bool initiallySet = false) noexcept;
     boolis_set() constnoexcept;
     voidset();
     voidreset() noexcept;
     <unspecified> operatorco_await() constnoexcept;
     };
    }

    例如:

    #include<cppcoro/single_consumer_event.hpp>cppcoro::single_consumer_event event;
    std::string value;
    cppcoro::task<> consumer()
    {
     // Coroutine will suspend here until some thread calls event.set()// eg. inside the producer() function below. co_await event;
     std::cout <<value <<std::endl;
    }voidproducer()
    {
     value = "foo";
     // This will resume the consumer() coroutine inside the call to set()// if it is currently suspended. event.set();
    }

    single_consumer_async_auto_reset_event

    此类提供一个异步同步原语,它允许单个协作者在事件通过调用 set() 方法发出信号之前等待。

    一旦在先前或者后续调用事件时等待事件的协同程序被释放,事件将自动重置为状态。

    这个类是一个更高效的async_auto_reset_event 版本,可以以在只有一个程序员等待事件时使用。 如果你需要在事件上支持多个并发等待协同程序,则使用 async_auto_reset_event 类。

    API摘要:

    // <cppcoro/single_consumer_async_auto_reset_event.hpp>namespacecppcoro{
     classsingle_consumer_async_auto_reset_event {
     public:single_consumer_async_auto_reset_event(
     bool initiallySet = false) noexcept;
     // Change the event to the 'set' state. If a coroutine is awaiting the// event then the event is immediately transitioned back to the 'not set'// state and the coroutine is resumed.voidset() noexcept;
     // Returns an Awaitable type that can be awaited to wait until// the event becomes 'set' via a call to the. set() method. If// the event is already in the 'set' state then the coroutine// continues without suspending.// The event is automatically reset back to the 'not set' state// before resuming the coroutine. Awaitable<void> operatorco_await() constnoexcept;
     };
    }

    使用方法的示例:

    std::atomic<int> value;
    cppcoro::single_consumer_async_auto_reset_event valueDecreasedEvent;
    cppcoro::task<> wait_until_value_is_below(int limit)
    {
     while (value.load(std::memory_order_relaxed)> = limit)
     {
     // Wait until there has been some change that we're interested in. co_await valueDecreasedEvent;
     }
    }voidchange_value(int delta)
    {
     value.fetch_add(delta, std::memory_order_relaxed);
     // Notify the waiter if there has been some change.if (delta <0) valueDecreasedEvent.set();
    }

    async_mutex

    为调用方提供简单的互斥抽象,允许调用者在协作器中对互斥锁进行操作,以便挂起协作器,直到获得。

    实现在等待互斥的程序中不会阻塞线程,而是将它的挂起,然后将它的恢复到 unlock()的调用。

    API摘要:

    // <cppcoro/async_mutex.hpp>namespacecppcoro{
     classasync_mutex_lock;
     classasync_mutex_lock_operation;
     classasync_mutex_scoped_lock_operation;
     classasync_mutex {
     public:async_mutex() noexcept;
     ~async_mutex();
     async_mutex(const async_mutex&) = delete;
     async_mutex& operator(const async_mutex&) = delete;
     booltry_lock() noexcept;
     async_mutex_lock_operation lock_async() noexcept;
     async_mutex_scoped_lock_operation scoped_lock_async() noexcept;
     voidunlock();
     };
     classasync_mutex_lock_operation {
     public:boolawait_ready() constnoexcept;
     boolawait_suspend(std::experimental::coroutine_handle<> awaiter) noexcept;
     voidawait_resume() constnoexcept;
     };
     classasync_mutex_scoped_lock_operation {
     public:boolawait_ready() constnoexcept;
     boolawait_suspend(std::experimental::coroutine_handle<> awaiter) noexcept;
     [[nodiscard]] async_mutex_lock await_resume() constnoexcept;
     };
     classasync_mutex_lock {
     public:// Takes ownership of the lock.async_mutex_lock(async_mutex& mutex, std::adopt_lock_t) noexcept;
     // Transfer ownership of the lock.async_mutex_lock(async_mutex_lock&& other) noexcept;
     async_mutex_lock(const async_mutex_lock&) = delete;
     async_mutex_lock& operator=(const async_mutex_lock&) = delete;
     // Releases the lock by calling unlock() on the mutex.~async_mutex_lock();
     };
    }

    使用方法的示例:

    #include<cppcoro/async_mutex.hpp>#include<cppcoro/task.hpp>#include<set>#include<string>cppcoro::async_mutex mutex;
    std::set<std::string> values;
    cppcoro::task<> add_item(std::string value)
    {
     cppcoro::async_mutex_lock lock = co_await mutex.scoped_lock_async();
     values.insert(std::move(value));
    }

    async_manual_reset_event

    手动重置事件是一个协同程序/线程同步原语,允许一个或者多个线程等待事件,直到线程通过调用 set()的线程发出信号。

    事件处于两种状态之一:'集合集'and'未设置'。

    如果事件处于非活动状态,则当协作器等待事件时,该状态将继续,而不会挂起。 但是,如果协同程序处于 '未设置' 状态,则coroutine将挂起,直到某个线程随后调用 set() 方法。

    在等待事件变为 '集合集'的任何线程都将在下一个线程的下一次调用中重新开始。

    注意,当事件被破坏时,你必须确保没有协同程序正在等待非阻塞的事件,因为它们将不会被恢复。

    例如:

    cppcoro::async_manual_reset_event event;
    std::string value;voidproducer()
    {
     value = get_some_string_value();
     // Publish a value by setting the event. event.set();
    }// Can be called many times to create many tasks.// All consumer tasks will wait until value has been published.cppcoro::task<> consumer()
    {
     // Wait until value has been published by awaiting event. co_await event;
     consume_value(value);
    }

    API摘要:

    namespacecppcoro{
     classasync_manual_reset_event_operation;
     classasync_manual_reset_event {
     public:async_manual_reset_event(bool initiallySet = false) noexcept;
     ~async_manual_reset_event();
     async_manual_reset_event(const async_manual_reset_event&) = delete;
     async_manual_reset_event(async_manual_reset_event&&) = delete;
     async_manual_reset_event& operator=(const async_manual_reset_event&) = delete;
     async_manual_reset_event& operator=(async_manual_reset_event&&) = delete;
     // Wait until the event becomes set. <unspecified> operatorco_await() constnoexcept;
     boolis_set() constnoexcept;
     voidset() noexcept;
     voidreset() noexcept;
     };
     classasync_manual_reset_event_operation {
     public:async_manual_reset_event_operation(async_manual_reset_event& event) noexcept;
     boolawait_ready() constnoexcept;
     boolawait_suspend(std::experimental::coroutine_handle<> awaiter) noexcept;
     voidawait_resume() constnoexcept;
     };
    }

    async_auto_reset_event

    自动重置事件是 coroutine/线程同步原语,允许一个或者多个线程通过调用 set() 来发出事件,直到事件被线程发出信号。

    在等待事件之前或者后续调用的协同程序释放事件之后,事件将自动重置回状态。

    API摘要:

    // <cppcoro/async_auto_reset_event.hpp>namespacecppcoro{
     classasync_auto_reset_event_operation;
     classasync_auto_reset_event {
     public:async_auto_reset_event(bool initiallySet = false) noexcept;
     ~async_auto_reset_event();
     async_auto_reset_event(const async_auto_reset_event&) = delete;
     async_auto_reset_event(async_auto_reset_event&&) = delete;
     async_auto_reset_event& operator=(const async_auto_reset_event&) = delete;
     async_auto_reset_event& operator=(async_auto_reset_event&&) = delete;
     // Wait for the event to enter the 'set' state.//// If the event is already 'set' then the event is set to the 'not set'// state and the awaiting coroutine continues without suspending.// Otherwise, the coroutine is suspended and later resumed when some// thread calls 'set()'.//// Note that the coroutine may be resumed inside a call to 'set()'// or inside another thread's call to 'operator co_await()'. async_auto_reset_event_operation operatorco_await() constnoexcept;
     // Set the state of the event to 'set'.//// If there are pending coroutines awaiting the event then one// pending coroutine is resumed and the state is immediately// set back to the 'not set' state.//// This operation is a no-op if the event was already 'set'.voidset() noexcept;
     // Set the state of the event to 'not-set'.//// This is a no-op if the state was already 'not set'.voidreset() noexcept;
     };
     classasync_auto_reset_event_operation {
     public:explicitasync_auto_reset_event_operation(async_auto_reset_event& event) noexcept;
     async_auto_reset_event_operation(const async_auto_reset_event_operation& other) noexcept;
     boolawait_ready() constnoexcept;
     boolawait_suspend(std::experimental::coroutine_handle<> awaiter) noexcept;
     voidawait_resume() constnoexcept;
     };
    }

    async_latch

    异步锁存是同步原语,允许协同程序异步等待直到计数器被减少到零。

    闩锁是单个使用对象。 一旦计数器达到零,门闩变成'就绪',并保持就绪直到闩锁被破坏。

    API摘要:

    // <cppcoro/async_latch.hpp>namespacecppcoro{
     classasync_latch {
     public:// Initialise the latch with the specified count.async_latch(std::ptrdiff_t initialCount) noexcept;
     // Query if the count has reached zero yet.boolis_ready() constnoexcept;
     // Decrement the count by n.// This will resume any waiting coroutines if the count reaches zero// as a result of this call.// It is undefined behaviour to decrement the count below zero.voidcount_down(std::ptrdiff_t n = 1) noexcept;
     // Wait until the latch becomes ready.// If the latch count is not yet zero then the awaiting coroutine will// be suspended and later resumed by a call to count_down() that decrements// the count to zero. If the latch count was already zero then the coroutine// continues without suspending. Awaiter<void> operatorco_await() constnoexcept;
     };
    }

    cancellation_token

    一个 cancellation_token 是一个可以传递给函数的值,允许调用者随后传递一个请求来取消该函数的操作。

    要获取能够取消的cancellation_token,首先必须创建 cancellation_source 对象。 cancellation_source::token() 方法可以用于制造链接到该 cancellation_source 对象的新 cancellation_token 值。

    当你希望以后取消某个操作时,你将 cancellation_token 传递给你可以调用 cancellation_source::request_cancellation() 在关联的cancellation_source 对象上。

    函数可以通过以下两种方式之一响应取消请求:

    • 通过调用任意时间间隔轮询取消 cancellation_token::is_cancellation_requested() 或者 cancellation_token::throw_if_cancellation_requested()
    • register 当使用 cancellation_registration 类请求取消时执行的回调。

    API摘要:

    namespacecppcoro{
     classcancellation_source {
     public:// Construct a new, independently cancellable cancellation source.cancellation_source();
     // Construct a new reference to the same cancellation state.cancellation_source(const cancellation_source& other) noexcept;
     cancellation_source(cancellation_source&& other) noexcept;
     ~cancellation_source();
     cancellation_source& operator=(const cancellation_source& other) noexcept;
     cancellation_source& operator=(cancellation_source&& other) noexcept;
     boolis_cancellation_requested() constnoexcept;
     boolcan_be_cancelled() constnoexcept;
     voidrequest_cancellation();
     cancellation_token token() constnoexcept;
     };
     classcancellation_token {
     public:// Construct a token that can't be cancelled.cancellation_token() noexcept;
     cancellation_token(const cancellation_token& other) noexcept;
     cancellation_token(cancellation_token&& other) noexcept;
     ~cancellation_token();
     cancellation_token& operator=(const cancellation_token& other) noexcept;
     cancellation_token& operator=(cancellation_token&& other) noexcept;
     boolis_cancellation_requested() constnoexcept;
     voidthrow_if_cancellation_requested() const;
     // Query if this token can ever have cancellation requested.// Code can use this to take a more efficient code-path in cases// that the operation does not need to handle cancellation.boolcan_be_cancelled() constnoexcept;
     };
     // RAII class for registering a callback to be executed if cancellation// is requested on a particular cancellation token.classcancellation_registration {
     public:// Register a callback to be executed if cancellation is requested.// Callback will be called with no arguments on the thread that calls// request_cancellation() if cancellation is not yet requested, or// called immediately if cancellation has already been requested.// Callback must not throw an unhandled exception when called.template<typename CALLBACK>
     cancellation_registration(cancellation_token token, CALLBACK&& callback);
     cancellation_registration(const cancellation_registration& other) = delete;
     ~cancellation_registration();
     };
     classoperation_cancelled : publicstd::exception
     {
     public:operation_cancelled();
     constchar* what() constoverride;
     };
    }

    示例:轮询方法

    cppcoro::task<> do_something_async(cppcoro::cancellation_token token)
    {
     // Explicitly define cancellation points within the function// by calling throw_if_cancellation_requested(). token.throw_if_cancellation_requested();
     co_await do_step_1();
     token.throw_if_cancellation_requested();
     do_step_2();
     // Alternatively, you can query if cancellation has been// requested to allow yourself to do some cleanup before// returning.if (token.is_cancellation_requested())
     {
     display_message_to_user("Cancelling operation...");
     do_cleanup();
     throw cppcoro::operation_cancelled{};
     }
     do_final_step();
    }

    示例:回调方法

    // Say we already have a timer abstraction that supports being// cancelled but it doesn't support cancellation_tokens natively.// You can use a cancellation_registration to register a callback// that calls the existing cancellation API. e.g.cppcoro::task<> cancellable_timer_wait(cppcoro::cancellation_token token)
    {
     auto timer = create_timer(10s);
     cppcoro::cancellation_registration registration(token, [&]
     {
     // Call existing timer cancellation API. timer.cancel();
     });
     co_await timer;
    }

    io_service

    io_service 类为从异步 I/O 操作处理 I/O 完成事件提供了抽象。

    异步 I/O 操作完成后,等待操作的程序集将在调用事件处理方法之一的I/O 线程上恢复: process_events()process_pending_events()process_one_event() 或者 process_one_pending_event()

    io_service 类不管理任何 I/O 线程。 必须确保某些线程调用coroutines的事件处理方法之一,等待 I/O 完成事件被调度。 这可能是一个专用线程,它调用 process_events() 或者与其他事件循环混合( 比如 。 通过调用 process_pending_events() 或者 process_one_pending_event() 定期轮询新事件的UI事件循环。

    这允许将 io_service 事件循环与其他事件循环( 如用户接口事件循环) 集成在一起。

    你可以通过多个线程调用 process_events() 来跨多个线程对事件进行多重处理。 你可以指定关于通过可选的io_service 构造函数参数主动处理事件的最大线程数的提示。

    在 Windows 上,实现使用 Windows I/O 完成端口工具以可以伸缩的方式将事件分派给 I/O 线程。

    API摘要:

    namespacecppcoro{
     classio_service {
     public:classschedule_operation;
     classtimed_schedule_operation;
     io_service();
     io_service(std::uint32_t concurrencyHint);
     io_service(io_service&&) = delete;
     io_service(const io_service&) = delete;
     io_service& operator=(io_service&&) = delete;
     io_service& operator=(const io_service&) = delete;
     ~io_service();
     // Scheduler methods [[nodiscard]]
     schedule_operation schedule() noexcept;
     template<typename REP, typename RATIO>
     [[nodiscard]]
     timed_schedule_operation schedule_after(
     std::chrono::duration<REP, RATIO> delay,
     cppcoro::cancellation_token cancellationToken = {}) noexcept;
     // Event-loop methods//// I/O threads must call these to process I/O events and execute// scheduled coroutines. std::uint64_tprocess_events();
     std::uint64_tprocess_pending_events();
     std::uint64_tprocess_one_event();
     std::uint64_tprocess_one_pending_event();
     // Request that all threads processing events exit their event loops.voidstop() noexcept;
     // Query if some thread has called stop()boolis_stop_requested() constnoexcept;
     // Reset the event-loop after a call to stop() so that threads can// start processing events again.voidreset();
     // Reference-counting methods for tracking outstanding references// to the io_service.//// The io_service::stop() method will be called when the last work// reference is decremented.//// Use the io_work_scope RAII class to manage calling these methods on// entry-to and exit-from a scope.voidnotify_work_started() noexcept;
     voidnotify_work_finished() noexcept;
     };
     classio_service::schedule_operation
     {
     public:schedule_operation(const schedule_operation&) noexcept;
     schedule_operation& operator=(const schedule_operation&) noexcept;
     boolawait_ready() constnoexcept;
     voidawait_suspend(std::experimental::coroutine_handle<> awaiter) noexcept;
     voidawait_resume() noexcept;
     };
     classio_service::timed_schedule_operation
     {
     public:timed_schedule_operation(timed_schedule_operation&&) noexcept;
     timed_schedule_operation(const timed_schedule_operation&) = delete;
     timed_schedule_operation& operator=(const timed_schedule_operation&) = delete;
     timed_schedule_operation& operator=(timed_schedule_operation&&) = delete;
     boolawait_ready() constnoexcept;
     voidawait_suspend(std::experimental::coroutine_handle<> awaiter);
     voidawait_resume();
     };
     classio_work_scope {
     public:io_work_scope(io_service& ioService) noexcept;
     io_work_scope(const io_work_scope& other) noexcept;
     io_work_scope(io_work_scope&& other) noexcept;
     ~io_work_scope();
     io_work_scope& operator=(const io_work_scope& other) noexcept;
     io_work_scope& operator=(io_work_scope&& other) noexcept;
     io_service& service() constnoexcept;
     };
    }

    例如:

    #include<cppcoro/task.hpp>#include<cppcoro/task.hpp>#include<cppcoro/io_service.hpp>#include<cppcoro/read_only_file.hpp>#include<experimental/filesystem>#include<memory>#include<algorithm>#include<iostream>namespacefs= std::experimental::filesystem;
    cppcoro::task<std::uint64_t> count_lines(cppcoro::io_service& ioService, fs::path path)
    {
     auto file = cppcoro::read_only_file::open(ioService, path);
     constexprsize_t bufferSize = 4096;
     auto buffer = std::make_unique<std::uint8_t[]>(bufferSize);
     std::uint64_t newlineCount = 0;
     for (std::uint64_t offset = 0, fileSize = file.size(); offset <fileSize;)
     {
     constauto bytesToRead = static_cast<size_t>(
     std::min<std::uint64_t>(bufferSize, fileSize - offset));
     constauto bytesRead = co_await file.read(offset, buffer.get(), bytesToRead);
     newlineCount += std::count(buffer.get(), buffer.get() + bytesRead, 'n');
     offset += bytesRead;
     }
     co_return newlineCount;
    }
    cppcoro::task<> run(cppcoro::io_service& ioService)
    {
     cppcoro::io_work_scope ioScope(ioService);
     auto lineCount = co_await count_lines(ioService, fs::path{"foo.txt"});
     std::cout <<"foo.txt has " <<lineCount <<" lines." <<std::endl;;
    }
    cppcoro::task<> process_events(cppcoro::io_service& ioService)
    {
     // Process events until the io_service is stopped.// ie. when the last io_work_scope goes out of scope. ioService.process_events();
     co_return;
    }intmain()
    {
     cppcoro::io_service ioService;
     cppcoro::sync_wait(cppcoro::when_all_ready(
     run(ioService),
     process_events(ioService)));
     return0;
    }

    io_service 作为调度程序

    一个 io_sevice 类实现了 SchedulerDelayedScheduler 概念的接口。

    这允许协同程序在当前线程上挂起执行,并调度自身以便在与特定 io_service 对象关联的I/O 线程上恢复。

    例如:

    cppcoro::task<> do_something(cppcoro::io_service& ioService)
    {
     // Coroutine starts execution on the thread of the task awaiter.// A coroutine can transfer execution to an I/O thread by awaiting the// result of io_service::schedule(). co_await ioService.schedule();
     // At this point, the coroutine is now executing on an I/O thread// inside a call to one of the io_service event processing methods.// A coroutine can also perform a delayed-schedule that will suspend// the coroutine for a specified duration of time before scheduling// it for resumption on an I/O thread. co_await ioService.schedule_after(100ms);
     // At this point, the coroutine is executing on a potentially different I/O thread.}

    filereadable_filewritable_file

    这些类型是执行具体文件i/o的抽象基类。

    API摘要:

    namespacecppcoro{
     classfile_read_operation;
     classfile_write_operation;
     classfile {
     public:virtual~file();
     std::uint64_tsize() const;
     protected:file(file&& other) noexcept;
     };
     classreadable_file : publicvirtual file
     {
     public: [[nodiscard]]
     file_read_operation read(
     std::uint64_t offset,
     void* buffer,
     std::size_t byteCount,
     cancellation_token ct = {}) constnoexcept;
     };
     classwritable_file : publicvirtual file
     {
     public:voidset_size(std::uint64_t fileSize);
     [[nodiscard]]
     file_write_operation write(
     std::uint64_t offset,
     constvoid* buffer,
     std::size_t byteCount,
     cancellation_token ct = {}) noexcept;
     };
     classfile_read_operation {
     public:file_read_operation(file_read_operation&& other) noexcept;
     boolawait_ready() constnoexcept;
     boolawait_suspend(std::experimental::coroutine_handle<> awaiter);
     std::size_tawait_resume();
     };
     classfile_write_operation {
     public:file_write_operation(file_write_operation&& other) noexcept;
     boolawait_ready() constnoexcept;
     boolawait_suspend(std::experimental::coroutine_handle<> awaiter);
     std::size_tawait_resume();
     };
    }

    read_only_filewrite_only_fileread_write_file

    这些类型表示具体的文件 I/O 类。

    API摘要:

    namespacecppcoro{
     classread_only_file : publicreadable_file {
     public: [[nodiscard]]
     static read_only_file open(
     io_service& ioService,
     const std::experimental::filesystem::path& path,
     file_share_mode shareMode = file_share_mode::read,
     file_buffering_mode bufferingMode = file_buffering_mode::default_);
     };
     classwrite_only_file : publicwritable_file {
     public: [[nodiscard]]
     static write_only_file open(
     io_service& ioService,
     const std::experimental::filesystem::path& path,
     file_open_mode openMode = file_open_mode::create_or_open,
     file_share_mode shareMode = file_share_mode::none,
     file_buffering_mode bufferingMode = file_buffering_mode::default_);
     };
     classread_write_file : publicreadable_file, publicwritable_file {
     public: [[nodiscard]]
     static read_write_file open(
     io_service& ioService,
     const std::experimental::filesystem::path& path,
     file_open_mode openMode = file_open_mode::create_or_open,
     file_share_mode shareMode = file_share_mode::none,
     file_buffering_mode bufferingMode = file_buffering_mode::default_);
     };
    }

    所有 open() 函数都在失败时引发 std::system_error

    用户定义函数

    sync_wait()

    sync_wait() 函数可以用于同步等待,直到指定的task 或者 shared_task 完成。

    如果任务还没有开始执行,那么它将在当前线程上启动。

    sync_wait() 调用将阻止任务完成并返回任务结果,如果任务完成,则返回任务的异常。

    sync_wait() 函数通常用于从 main() 中启动顶级任务并等待任务完成,在练习中是启动第一个/顶级 task的唯一方法。

    API摘要:

    // <cppcoro/sync_wait.hpp>namespacecppcoro{
     template<typename TASKS>
     decltype(auto) sync_wait(TASK&& task);
    }

    例如:

    voidexample_task()
    {
     auto makeTask = []() -> task<std::string>
     {
     co_return "foo";
     };
     auto task = makeTask();
     // start the lazy task and wait until it completessync_wait(task) == "foo";
     sync_wait(makeTask()) == "foo";
    }voidexample_shared_task()
    {
     auto makeTask = []() -> shared_task<std::string>
     {
     co_return "foo";
     };
     auto task = makeTask();
     // start the shared task and wait until it completessync_wait(task) == "foo";
     sync_wait(makeTask()) == "foo";
    }

    when_all_ready()

    when_all_ready() 函数可以用于创建新的task,当所有指定的输入任务完成时,该将完成。

    输入任务可以是 task<T> 或者 shared_task<T>

    当返回的taskco_await 时,它将开始执行每个输入任务,按照传递给 when_all_ready() 函数的顺序执行等待线程。 如果这些任务没有同步完成,那么它们将并发执行。

    完成所有输入任务后,返回的task 将完成并恢复等待的协同程序。 等待协同程序将在最后完成的输入任务的线程上继续。

    保证返回的task 不会在 co_await with时引发异常,即使某些输入任务失败,也不会发生异常。

    注意,如果 when_all_ready() 调用本身无法为帧返回的task 程序集分配内存,则可以能会引发 std::bad_alloc

    完成后,输入任务返回到等待的协同程序。 这允许调用方同步执行协程并同步它的完成,同时保持成功/失败的每个输入任务的结果。

    when_all()的区别在于,co_awaittask<T>::when_ready()co_await'ing the task`不同。

    API摘要:

    // <cppcoro/when_all_ready.hpp>namespacecppcoro{
     template<typename... TASKS>
     task<std::tuple<TASKS...>> when_all_ready(TASKS... tasks);
     template<typename T>
     task<std::vector<task<T>> when_all_ready(
     std::vector<task<T>> tasks);
     template<typename T>
     task<std::vector<shared_task<T>> when_all_ready(
     std::vector<shared_task<T>> tasks);
    }

    使用方法的示例:

    task<std::string> get_record(int id);
    task<> example1()
    {
     // Run 3 get_record() operations concurrently and wait until they're all ready.// Returns a std::tuple of tasks that can be unpacked using structured bindings.auto [task1, task2, task3] = co_await when_all_ready(
     get_record(123),
     get_record(456),
     get_record(789));
     // Unpack the result of each task (this will complete immediately) std::string& record1 = co_await task1;
     std::string& record2 = co_await task2;
     std::string& record3 = co_await task3;
     // Use records....}
    task<> example2()
    {
     // Create the input tasks. They don't start executing yet. std::vector<task<std::string>> tasks;
     for (int i = 0; i <1000; ++i)
     {
     tasks.emplace_back(get_record(i));
     }
     // Execute all tasks concurrently.// Returns the input vector of tasks. tasks = co_await when_all_ready(std::move(tasks));
     // Unpack and handle each result individually once they're all complete.for (int i = 0; i <1000; ++i)
     {
     try {
     std::string& record = co_await tasks[i];
     std::cout <<i <<" = " <<record <<std::endl;
     }
     catch (const std::exception& ex)
     {
     std::cout <<i <<" : " <<ex.what() <<std::endl;
     }
     }
    }

    when_all()

    when_all() 函数可以用于创建一个新的task,当所有输入任务完成后,将返回所有结果的聚合。

    等待返回的task 时,它将开始执行当前线程上的所有输入任务。 一旦第一个任务暂停,第二个任务就会启动,等等。 任务并发执行,直到它们全部运行完成。

    一旦所有输入任务都运行完毕,结果将从每个单独的任务结果中生成。 在输入任务抛出异常时,如果聚合结果的构造引发异常,则异常将传播到返回的异常的co_await 中。

    如果多个任务失败,那么它的中一个异常将传播到 when_all() 任务中,它的他异常将被忽略。 没有指定要选择哪个例外。 如果知道哪个任务失败很重要,那么应该使用 when_all_ready(),并将每个任务的结果单独地进行处理。

    API摘要:

    // <cppcoro/when_all.hpp>namespacecppcoro{
     // Variadic version.template<typename... TASKS>
     task<std::tuple<typename TASKS::value_type...>> when_all(TASKS... tasks);
     // Overloads for vector of value-returning taskstemplate<typename T>
     task<std::vector<T>> when_all(std::vector<task<T>> tasks);
     template<typename T>
     task<std::vector<T>> when_all(std::vector<shared_task<T>> tasks);
     // Overloads for vector of reference-returning taskstemplate<typename T>
     task<std::vector<std::reference_wrapper<T>>> when_all(std::vector<task<T&>> tasks);
     template<typename T>
     task<std::vector<std::reference_wrapper<T>>> when_all(std::vector<shared_task<T&>> tasks);
     // Overloads for vector of void-returning tasks task<> when_all(std::vector<task<>> tasks);
     task<> when_all(std::vector<shared_task<>> tasks);
    }

    例如:

    task<A> get_a();
    task<B> get_b();
    task<> example1()
    {
     // Run get_a() and get_b() concurrently.// Task yields a std::tuple<A, B> which can be unpacked using structured bindings.auto [a, b] = co_await when_all(get_a(), get_b());
     // use a, b}
    task<std::string> get_record(int id);
    task<> example2()
    {
     std::vector<task<std::string>> tasks;
     for (int i = 0; i <1000; ++i)
     {
     tasks.emplace_back(get_record(i));
     }
     // Concurrently execute all get_record() tasks.// If any of them fail with an exception then the exception will propagate// out of the co_await expression once they have all completed. std::vector<std::string> records = co_await when_all(std::move(tasks));
     // Process resultsfor (int i = 0; i <1000; ++i)
     {
     std::cout <<i <<" = " <<records[i] <<std::endl;
     }
    }

    fmap()

    函数可用来将可以调用函数应用到容器类型中包含的值,返回应用该函数所包含的函数的新容器类型的结果。

    fmap() 函数可以对 task<T>shared_task<T>generator<T>recursive_generator<T>async_generator<T> 类型的值应用函数。

    这些类型都为 fmap() 提供了一个重载,它接受两个参数;一个应用的函数和容器值。 有关支持的fmap() 重载的每种类型,请参阅文档。

    例如,fmap() 函数可以用于将函数应用到 task<T>的最终结果,生成一个新的task<U>,该函数返回函数。

    // Given a function you want to apply that converts// a value of type A to value of type B.B a_to_b(A value);// And a task that yields a value of type Acppcoro::task<A> get_an_a();// We can apply the function to the result of the task using fmap()// and obtain a new task yielding the result.cppcoro::task<B> bTask = fmap(a_to_b, get_an_a());// An alternative syntax is to use the pipe notation.cppcoro::task<B> bTask = get_an_a() | cppcoro::fmap(a_to_b);

    API摘要:

    // <cppcoro/fmap.hpp>namespacecppcoro{
     template<typename FUNC>
     structfmap_transform {
     fmap_transform(FUNC&& func) noexcept(std::is_nothrow_move_constructible_v<FUNC>);
     FUNC func;
     };
     // Type-deducing constructor for fmap_transform object that can be used// in conjunction with operator|.template<typename FUNC>
     fmap_transform<FUNC> fmap(FUNC&& func);
     // operator| overloads for providing pipe-based syntactic sugar for fmap()// such that the expression:// <value-expr> | cppcoro::fmap(<func-expr>)// is equivalent to:// fmap(<func-expr>, <value-expr>)template<typename T, typename FUNC>
     decltype(auto) operator|(T&& value, fmap_transform<FUNC>&& transform);
     template<typename T, typename FUNC>
     decltype(auto) operator|(T&& value, fmap_transform<FUNC>& transform);
     template<typename T, typename FUNC>
     decltype(auto) operator|(T&& value, const fmap_transform<FUNC>& transform);
    }

    fmap() 函数旨在按参数依赖查找( ADL ) 查找正确重载,因此通常应该在不使用 cppcoro:: 前缀的情况下调用它。

    resume_on()

    可以以使用 resume_on() 函数控制 taskshared_task 或者 async_generator 应继续它的等待程序的执行上下文。

    通常情况下,task 或者 async_generator的awaiter将继续执行任何在 task 上完成的线程的执行。 在某些情况下,这可能不是你要继续执行的线程。 对于这些情况,可以使用 resume_on() 函数创建新的任务或者生成器,以便在与指定调度程序关联的线程上恢复执行。

    resume_on() 函数可以用作返回新任务/生成器的正常函数。 或者它可以在管道语法中使用。

    例如:

    task<record> load_record(int id);
    ui_thread_scheduler uiThreadScheduler;
    task<> example()
    {
     // This will start load_record() on the current thread.// Then when load_record() completes (probably on an I/O thread)// it will reschedule execution onto thread pool and call to_json// Once to_json completes it will transfer execution onto the// ui thread before resuming this coroutine and returning the json text. task<std::string> jsonTask =
     load_record(123)
     | cppcoro::resume_on(threadpool::default())
     | cppcoro::fmap(to_json)
     | cppcoro::resume_on(uiThreadScheduler);
     // At this point, all we've done is create a pipeline of tasks.// The tasks haven't started executing yet.// Await the result. Starts the pipeline of tasks. std::string jsonText = co_await jsonTask;
     // Guaranteed to be executing on ui thread here. someUiControl.set_text(jsonText);
    }

    API摘要:

    // <cppcoro/resume_on.hpp>namespacecppcoro{
     template<typename SCHEDULER, typename T>
     task<T> resume_on(SCHEDULER& scheduler, task<T> t);
     template<typename SCHEDULER, typename T>
     task<T> resume_on(SCHEDULER& scheduler, shared_task<T> t);
     template<typename SCHEDULER, typename T>
     async_generator<T> resume_on(SCHEDULER& scheduler, async_generator<T> source);
     template<typename SCHEDULER>
     structresume_on_transform {
     explicitresume_on_transform(SCHEDULER& scheduler) noexcept;
     SCHEDULER& scheduler;
     };
     // Construct a transform/operation that can be applied to a source object// using"pipe" notation (ie. operator|).template<typename SCHEDULER>
     resume_on_transform<SCHEDULER> resume_on(SCHEDULER& scheduler) noexcept;
     // Equivalent to 'resume_on(transform.scheduler, std::forward<T>(value))'template<typename T, typename SCHEDULER>
     decltype(auto) operator|(T&& value, resume_on_transform<SCHEDULER> transform)
     {
     returnresume_on(transform.scheduler, std::forward<T>(value));
     }
    }

    schedule_on()

    schedule_on() 函数可以用于更改给定 task 或者 async_generator 开始执行的执行上下文。

    当应用到 async_generator 时,它还会影响在 co_yield 语句之后它所恢复的执行上下文。

    请注意,schedule_on 转换不指定 task 或者 async_generator 将完成或者 yield 结果的线程,这取决于实现协议。

    有关控制操作完成的线程的转换,请参见 resume_on() 运算符。

    例如:

    task<int> get_value();
    io_service ioSvc;
    task<> example()
    {
     // Starts executing get_value() on the current thread.int a = co_await get_value();
     // Starts executing get_value() on a thread associated with ioSvc.int b = co_await schedule_on(ioSvc, get_value());
    }

    API摘要:

    // <cppcoro/schedule_on.hpp>namespacecppcoro{
     // Return a task that yields the same result as 't' but that// ensures that 't' is co_await'ed on a thread associated with// the specified scheduler. Resulting task will complete on// whatever thread 't' would normally complete on.template<typename SCHEDULER, typename T>
     task<T> schedule_on(SCHEDULER& scheduler, task<T> t);
     // Return a generator that yields the same sequence of results as// 'source' but that ensures that execution of the coroutine starts// execution on a thread associated with 'scheduler' and resumes// after a 'co_yield' on a thread associated with 'scheduler'.template<typename SCHEDULER, typename T>
     async_generator<T> schedule_on(SCHEDULER& scheduler, async_generator<T> source);
     template<typename SCHEDULER>
     structschedule_on_transform {
     explicitschedule_on_transform(SCHEDULER& scheduler) noexcept;
     SCHEDULER& scheduler;
     };
     template<typename SCHEDULER>
     schedule_on_transform<SCHEDULER> schedule_on(SCHEDULER& scheduler) noexcept;
     template<typename T, typename SCHEDULER>
     decltype(auto) operator|(T&& value, schedule_on_transform<SCHEDULER> transform);
    }
    概念

    Scheduler 概念

    一个 Scheduler 是一个概念,允许在某些执行上下文中调度协同执行。

    concept Scheduler
    {
     <awaitable-type> schedule();
    }

    给定类型 S,实现 Scheduler 概念,以及类型 S的实例 S:

    • s.schedule() 方法返回一个awaitable类型,这样 co_await s.schedule() 将无条件暂停当前 coroutine,并调度它以便在与调度程序。S 和调度程序关联的执行上下文上恢复。
    • co_await s.schedule() 表达式的结果有类型 void
    cppcoro::task<> f(Scheduler& scheduler)
    {
     // Execution of the coroutine is initially on the caller's execution context.// Suspends execution of the coroutine and schedules it for resumption on// the scheduler's execution context. co_await scheduler.schedule();
     // At this point the coroutine is now executing on the scheduler's// execution context.}

    DelayedScheduler 概念

    一个 DelayedScheduler 是一个概念,它允许程序员在执行指定的时间之后在执行调度程序上下文时安排它。

    concept DelayedScheduler : Scheduler
    {
     template<typename REP, typename RATIO>
     <awaitable-type> schedule_after(std::chrono::duration<REP, RATIO> delay);
     template<typename REP, typename RATIO>
     <awaitable-type> schedule_after(
     std::chrono::duration<REP, RATIO> delay,
     cppcoro::cancellation_token cancellationToken);
    }

    给定一个类型 S,实现 DelayedScheduler 和实例,S 类型为 S:

    • 这个方法返回一个对象,在执行与调度程序关联的执行上下文之前,在执行关联之前,co_await s.schedule_after(delay) 会等待当前的协同程序持续一段时间,以便继续执行。
    • co_await s.schedule_after(delay) 表达式具有类型 void
    建筑

    支持使用 Visual Studio 和Clang在 Windows 下构建cppcoro库,并带有 Clang + 。

    这个库使用 Cake 构建系统 ( 不,不是 C# 。) 。

    Cake 构建系统被自动签出为git子模子,因这里不需要单独下载或者安装它。

    基于 Windows的

    这里库当前需要 Visual Studio 2017或者更高版本,以及 Windows 10 SDK 。

    计划对 Clang ( #3 ) 和 Linux ( #15 ) 进行支持。

    先决条件

    Cake 构建系统在 python 中实现,需要安装 python 2.7.

    确保 python 2.7解释器在你的路径中,并作为'python'可用。

    确保安装 Visual Studio 2017更新 3或者更高版本。 注意,更新 2或者更早版本中的协同程序存在一些已知问题,这些问题已经在更新 3中修复。

    你还可以通过从 https://vcppdogfooding.azurewebsites.net/下载一个NuGet包,并将. nuget 文件解压到一个目录中,来使用 Visual Studio 编译器的一个实验版本。 修改 config.cake 文件,通过修改和取消注释以下行,指向解压位置:

    nugetPath =None# r'C:PathToVisualCppTools.14.0.25224-Pre'

    确保已经安装 Windows 10 SDK 。 默认情况下,它将使用最新的Windows 10 SDK和通用运行时版本。

    克隆存储库

    cppcoro库利用git模块来提取 Cake 构建系统的源代码。

    这意味着你需要将 --recursive 标志传递给 git clone 命令。 比如,

    c:Code> git clone --recursive https://github.com/lewissbaker/cppcoro.git

    如果已经克隆了 cppcoro,则应在提取更改后更新子模块。

    c:Codecppcoro> git submodule update --init --recursive

    从命令行生成

    要从命令行生成,只需在工作空间 root 中运行'cake.bat'。

    比如,

    C:cppcoro> cake.bat
    Building with C:cppcoroconfig.cake - Variant(release='debug', platform='windows', architecture='x86', compilerFamily='msvc', compiler='msvc14.10')
    Building with C:cppcoroconfig.cake - Variant(release='optimised', platform='windows', architecture='x64', compilerFamily='msvc', compiler='msvc14.10')
    Building with C:cppcoroconfig.cake - Variant(release='debug', platform='windows', architecture='x64', compilerFamily='msvc', compiler='msvc14.10')
    Building with C:cppcoroconfig.cake - Variant(release='optimised', platform='windows', architecture='x86', compilerFamily='msvc', compiler='msvc14.10')
    Compiling testmain.cpp
    Compiling testmain.cpp
    Compiling testmain.cpp
    Compiling testmain.cpp
    ...
    Linking buildwindows_x86_msvc14.10_debugtestrun.exe
    Linking buildwindows_x64_msvc14.10_optimisedtestrun.exe
    Linking buildwindows_x86_msvc14.10_optimisedtestrun.exe
    Linking buildwindows_x64_msvc14.10_debugtestrun.exe
    Generating code
    Finished generating code
    Generating code
    Finished generating code
    Build succeeded.
    Build took 0:00:02.419.

    默认情况下,运行不带参数的cake 将生成所有生成变量的项目,并执行单元测试。 你可以通过传递额外的命令行参数来缩小生成的内容。 比如,

    c:cppcoro> cake.bat release=debug architecture=x64 lib/build.cake
    Building with C:UsersLewisCodecppcoroconfig.cake - Variant(release='debug', platform='windows', architecture='x64', compilerFamily='msvc', compiler='msvc14.10')
    Archiving buildwindows_x64_msvc14.10_debuglibcppcoro.lib
    Build succeeded.
    Build took 0:00:00.321.

    你可以运行 cake --help 到 List 可用的命令行选项。

    生成 Visual Studio 项目文件

    若要从 Visual Studio 内部开发,可以通过运行 cake.bat -p 来生成。vcproj/。sln文件。

    比如,

    c:cppcoro> cake.bat -p
    Building with C:cppcoroconfig.cake - Variant(release='debug', platform='windows', architecture='x86', compilerFamily='msvc', compiler='msvc14.10')
    Building with C:cppcoroconfig.cake - Variant(release='optimised', platform='windows', architecture='x64', compilerFamily='msvc', compiler='msvc14.10')
    Building with C:cppcoroconfig.cake - Variant(release='debug', platform='windows', architecture='x64', compilerFamily='msvc', compiler='msvc14.10')
    Building with C:cppcoroconfig.cake - Variant(release='optimised', platform='windows', architecture='x86', compilerFamily='msvc', compiler='msvc14.10')
    Generating Solution build/project/cppcoro.sln
    Generating Project build/project/cppcoro_tests.vcxproj
    Generating Filters build/project/cppcoro_tests.vcxproj.filters
    Generating Project build/project/cppcoro.vcxproj
    Generating Filters build/project/cppcoro.vcxproj.filters
    Build succeeded.
    Build took 0:00:00.247.

    当你从 Visual Studio 内部构建这些项目时,它将调用 Cake 来执行编译。

    基于Linux的插件

    还可以使用 Clang + libc++ 5.0或者更高版本在Linux下构建cppcoro项目。

    已经在 Ubuntu 17.04下测试了 cppcoro 。

    Prerequisities

    确保安装了以下软件包:

    • python 2.7
    • Clang> = 5.0
    • LLD> = 5.0
    • libc++> = 5.0

    构建 cppcoro

    假设你已经构建并安装了Clang和 libc++ 。

    如果你还没有配置 dtmf,请参阅以下部分,有关设置cppcoro为建筑构建dtmf的详细信息。

    检查cppcoro及其子模块:

    git clone --recursive https://github.com/lewissbaker/cppcoro.git cppcoro

    运行 init.sh 来设置 cake bash函数:

    cd cppcoro
    source init.sh

    然后可以从工作区 root 运行 cake,以生成cppcoro并运行测试:

    $ cake

    你可以指定其他命令行参数来自定义生成:

    • --help 将打印命令行参数的帮助
    • --debug=run 将显示正在运行的生成命令行
    • release=debug 或者 release=optimised 将将生成变量限制为调试或者优化的( 默认情况下,它将生成两个) 。
    • lib/build.cake 只构建cppcoro库,而不是测试。
    • test/build.cake@task_tests.cpp 只编译一个特定的源文件

    例如:

    $ cake --debug=run release=debug lib/build.cake

    自定义Clang的位置

    如果你的dtmf编译器没有位于 /usr/bin/clang,那么你需要修改 config.cake 文件来告诉 Cake 在哪里找到 dtmf 。

    config.cake 中编辑以下行:

    # If you have built your own version of Clang, you can modify# this variable to point to the CMAKE_INSTALL_PREFIX for# where you have installed your clang/libcxx build. clangInstallPrefix ='/usr'

    如果 libc++ 安装在其他位置,那么你可以通过修改 config.cake 中的以下行来定制它的位置。

    # Set this to the install-prefix of where libc++ is installed.# You only need to set this if it is not installed at the same# location as clangInstallPrefix. libCxxInstallPrefix =None# '/path/to/install'

    :安装位置有多个Clang并且你要使用的是 not,那么可以通过修改文件指定要使用的Clang二进制文件的NAME 来明确指定要使用的install

     compiler = ClangCompiler(
     configuration=configuration,
     clangExe=cake.path.join(clangBinPath, 'clang-6.0'),
     llvmArExe=cake.path.join(clangBinPath, 'llvm-ar-6.0'),
     binPaths=[clangBinPath])

    使用Clang的快照生成

    如果Linux发行版没有Clang版本或者更高版本,你可以从LLVM项目安装快照构建。

    按照 http://apt.llvm.org/的指示设置软件包管理器以支持从LLVM包管理器中提取。

    例如对于 Ubuntu 17.04 Zesty:

    编辑 /etc/apt/sources.list 并添加以下行:

    deb http://apt.llvm.org/zesty/llvm-toolchain-zesty main
    deb-src http://apt.llvm.org/zesty/llvm-toolchain-zesty main

    为这些软件包安装PGP密钥:

    $ wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -

    安装Clang和 LLD:

    $ sudo apt-get install clang-6.0 lld-6.0

    LLVM快照构建不包括libc++版本,所以你需要自己构建。 请参见下面。

    构建你自己的Clang

    你也可以通过从源代码构建Clang来使用出血边缘Clang版本。

    请参阅这里的说明:

    为此,你需要安装以下先决条件:

    $ sudo apt-get install git cmake ninja-build clang lld

    注意,我们使用你的clang的分布版本来构建来自源的clang 。 GCC也可以在这里使用。

    检查 LLVM + Clang + LLD + libc++库:

    mkdir llvm
    cd llvm
    git clone --depth=1 https://github.com/llvm-mirror/llvm.git llvm
    git clone --depth=1 https://github.com/llvm-mirror/clang.git llvm/tools/clang
    git clone --depth=1 https://github.com/llvm-mirror/lld.git llvm/tools/lld
    git clone --depth=1 https://github.com/llvm-mirror/libcxx.git llvm/projects/libcxx
    ln -s llvm/tools/clang clang
    ln -s llvm/tools/lld lld
    ln -s llvm/projects/libcxx libcxx

    配置和构建 Clang:

    mkdir clang-build
    cd clang-build
    cmake -GNinja 
     -DCMAKE_CXX_COMPILER=/usr/bin/clang++ 
     -DCMAKE_C_COMPILER=/usr/bin/clang 
     -DCMAKE_BUILD_TYPE=MinSizeRel 
     -DCMAKE_INSTALL_PREFIX="/path/to/clang/install"
     -DCMAKE_BUILD_WITH_INSTALL_RPATH="yes" 
     -DLLVM_TARGETS_TO_BUILD=X86 
     -DLLVM_ENABLE_PROJECTS="lld;clang" 
    . . /llvm
    ninja install-clang 
     install-clang-headers 
     install-llvm-ar 
     install-lld

    构建 libc+ +

    cppcoro项目需要 libc++,因为它包含在Clang下使用 C++ 协同程序所需的<experimental/coroutine> 头。

    签出 libc++ + llvm:

    mkdir llvm
    cd llvm
    git clone --depth=1 https://github.com/llvm-mirror/llvm.git llvm
    git clone --depth=1 https://github.com/llvm-mirror/libcxx.git llvm/projects/libcxx
    ln -s llvm/projects/libcxx libcxx

    生成 libc++:

    mkdir libcxx-build
    cd libcxx-build
    cmake -GNinja 
     -DCMAKE_CXX_COMPILER="/path/to/clang/install/bin/clang++" 
     -DCMAKE_C_COMPILER="/path/to/clang/install/bin/clang" 
     -DCMAKE_BUILD_TYPE=Release 
     -DCMAKE_INSTALL_PREFIX="/path/to/clang/install" 
     -DLLVM_PATH="../llvm" 
     -DLIBCXX_CXX_ABI=libstdc++ 
     -DLIBCXX_CXX_ABI_INCLUDE_PATHS="/usr/include/c++/6.3.0/;/usr/include/x86_64-linux-gnu/c++/6.3.0/" 
    . . /libcxx
    ninja cxx
    ninja install

    这将在安装clang的同一安装目录中构建并安装 libc++ 。

    支持

    GitHub问题是支持。Bug 报告和特性请求的主要机制。

    欢迎捐赠,请求请求将被愉快地审查。 你同意许可,我只要求你同意在MIT许可下所做的任何贡献。

    在 C++ coroutines中,如果你有一般问题,通常可以在 Cpplang松弛组的#coroutines 通道中找到someone帮助,。




    Copyright © 2011 HelpLib All rights reserved.    知识分享协议 京ICP备05059198号-3  |  如果智培  |  酷兔英语