Home DEVELOPER C++ programming language: concurrent with std::execute

C++ programming language: concurrent with std::execute

0


There has been a change in plans for this post. My original plan was to introduce the C++26 library after the main language. However, the implementation state of the library is not complete enough.

Advertisement





Rainer Grimm has been working as a software architect, team and training manager for many years. He enjoys writing articles on the programming languages ​​C++, Python, and Haskell, but also frequently speaks at expert conferences. On his blog Modern C++ he discusses his passion C++ in depth.

That’s why I decided to go with Concurrency and std::execution To continue. I will introduce the remaining features of C++26 as soon as a compiler implements them.



Ticwatch Atlas Smart Watch by Mobvoi

std::execution It has three core essences: scheduler, sender and receiver, and a set of customizable asynchronous algorithms. my presentation of std::execution based on proposal P2300R10,

first experiment

For my first experiments I have stdexec used. This reference from Nvidia is based on the eighth revision of the implementation proposal. The purpose of this experiment can be found on GitHub:

  1. Provide proof of concept for implementation of the proposed design in p2300,
  2. Provide early access to developers wanting to experiment with the sender model.
  3. Collaborate with those interested in participating or contributing to the design of the P2300 (contributions welcome!).

can do stdexec But godbolt Try with the following programs:

#include 
#include 

int main()
{
    // Declare a pool of 3 worker threads:
    exec::static_thread_pool pool(3);

    // Get a handle to the thread pool:
    auto sched = pool.get_scheduler();

    // Describe some work:
    // Creates 3 sender pipelines that are executed concurrently by passing to `when_all`
    // Each sender is scheduled on `sched` using `on` and starts with `just(n)` that creates a
    // Sender that just forwards `n` to the next sender.
    // After `just(n)`, we chain `then(fun)` which invokes `fun` using the value provided from `just()`
    // Note: No work actually happens here. Everything is lazy and `work` is just an object that statically
    // represents the work to later be executed
    auto fun = ()(int i) { return i*i; };
    auto work = stdexec::when_all(
        stdexec::starts_on(sched, stdexec::just(0) | stdexec::then(fun)),
        stdexec::starts_on(sched, stdexec::just(1) | stdexec::then(fun)),
        stdexec::starts_on(sched, stdexec::just(2) | stdexec::then(fun))
    );

    // Launch the work and wait for the result
    auto (i, j, k) = stdexec::sync_wait(std::move(work)).value();

    // Print the results:
    std::printf("%d %d %d\n", i, j, k);
}

I will convert this program to revision 10 syntax. The program starts by including the required headers:exec/static_thread_pool.hpp>To create a thread pool,stdexec/execution.hpp>For execution-related services. In main-The program will continue static_thread_pool pool Made of eight threads. Thread pool executes tasks concurrently. The get_scheduler member function of the thread pool is called to obtain the scheduler object. sched To get. The scheduler schedules tasks in the thread pool.

lambda function fun takes an integer i as input and returns its square (i * i) Back. This lambda is applied to the input values ​​in subsequent functions. Celebration stdexec::when_all Creates a task that waits for multiple subtasks to complete. Each subtask comes with a function stdexec::starts_on Creates a task on the specified scheduler sched Plans. Celebration stdexec::just Creates a function that produces a single value (0, 1 or 2) and the function stdexec::then is used to do this fun-Apply lambda to this value. The resulting work object becomes work called.

Celebration stdexec::sync_wait Then it is asked to wait synchronously until the task is completed. Celebration std::move transfers ownership of the work work To sync_wait, value-member function is used to return the result sync_wait Called to get the values ​​generated by sub-functions. These values ​​are in variables i, yung k Opened it.

Finally returns the value of the program i, j And k with std::printf On the console. These values ​​represent the classes of 0, 1 and 2 respectively.

The following screenshot shows the program running in Compiler Explorer:



I wrote at the beginning of this article that std::execution has three major abstractions: a scheduler, a sender and a receiver, and a set of customizable asynchronous algorithms.

execution resource

  • represent the place of execution and
  • No representation is needed in the code.

scheduler: Schedule

  • Represents the execution resource.
  • The scheduler concept is defined by a single dispatcher algorithm: schedule,
  • algorithm schedule Returns a dispatcher that runs on the execution resource specified by the scheduler.

Sender describes the task, when_all, starts_on, just, then

  • sends some values ​​when a receiver connected to this transmitter eventually receives these values,
  • just There is a so-called transmitter factory.

Recipient stops workflow:sync_wait

  • It supports three channels: value, error, stopped.
  • This is a so-called sender consumer.
  • This submits the task and blocks the current std::thread And upon completion returns an optional tuple of the values ​​sent by the provided sender.

After this introduction, I will discuss a series of customizable asynchronous algorithms in more detail and provide additional examples.


(rme)

C++ programming language: concurrent with std::execute

NO COMMENTS

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Exit mobile version