C++ Programlama Dili: std::execution ile eşzamanlılık
Bu yazı için planlarda bir değişiklik var. Asıl planım ana dilden sonra C++26 kütüphanesini tanıtmaktı. Ancak kütüphanenin uygulama durumu yeterince tamamlanmamıştır.
Duyuru
Rainer Grimm uzun yıllardır yazılım mimarı, ekip ve eğitim yöneticisi olarak çalışmaktadır. C++, Python ve Haskell programlama dilleri üzerine makaleler yazmaktan hoşlanıyor, aynı zamanda özel konferanslarda sık sık konuşmaktan da hoşlanıyor. Modern C++ adlı blogunda C++ tutkusunu yoğun bir şekilde ele alıyor.
Bu yüzden Eşzamanlılık e'yi kullanmaya karar verdim std::execution devam etmek. C++26'nın geri kalan özelliklerini derleyici uygulamaya koyar koymaz tanıtacağım.
Aufbau von std::execution
std::execution üç temel soyutlamaya sahiptir: zamanlayıcı, gönderen ve alıcı ve bir dizi özelleştirilebilir asenkron algoritma. Benim sunumum std::execution P2300R10 önerisine dayanmaktadır.
İlk deneyler
İlk deneylerimde stdexec kullandım. Nvidia'nın bu referans uygulaması, teklifin sekizinci revizyonuna dayanmaktadır. Bu deneyin kapsamını GitHub'da bulabilirsiniz:
#include <stdexec/execution.hpp>
#include <exec/static_thread_pool.hpp>
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` that creates a
// Sender that just forwards `n` to the next sender.
// After `just`, 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:rintf("%d %d %dn", i, j, k);
}
Bu programı revizyon 10 sözdizimine dönüştüreceğim. Program gerekli başlıkları ekleyerek başlıyor:exec/static_thread_pool.hpp> bir iş parçacığı havuzu oluşturmak için,stdexec/execution.hpp> yürütmeyle ilgili hizmetler için. içinde main-Fonksiyon aktif olacak static_thread_pool pool sekiz iş parçacığı ile oluşturulmuştur. İş parçacığı havuzu görevleri aynı anda yürütür. Bir zamanlayıcı nesnesi almak için iş parçacığı havuzu üye işlevi get_scheduler çağrılır sched elde etmek. Zamanlayıcı, iş parçacığı havuzundaki görevleri zamanlar.
Lambda işlevi fun bir tamsayı alır i girdi olarak ve karesini döndürür (i * i) Geriye doğru. Bu lambda sonraki görevlerde giriş değerlerine uygulanır. fonksiyon stdexec::when_all birden çok alt görevin tamamlanmasını bekleyen bir görev oluşturur. Her alt görev bu özellikle birlikte gelir stdexec::starts_on belirtilen zamanlayıcıda görevi oluşturur sched planlar. fonksiyon stdexec::just tek bir değer (0, 1 veya 2) üreten bir görev oluşturur ve işlev stdexec::then bunu yapmak için kullanılır fun-Bu değere lambda uygulayın. Ortaya çıkan görev nesnesi şöyle olur: work isminde.
fonksiyon stdexec::sync_wait daha sonra görevin tamamlanmasını eşzamanlı olarak beklemek için çağrılır. fonksiyon std::move işletmenin mülkiyetini devreder work İLE sync_wait. THE value-Üye işlevi sonuç için kullanılır sync_wait alt görevler tarafından oluşturulan değerleri almak için çağrılır. Bu değerler değişkenlerdedir i, Yung k ambalajından çıkarıldı.
Son olarak program aşağıdaki değerleri döndürür: i, j VE k ile std:rintf Konsolda. Bu değerler sırasıyla 0, 1 ve 2'nin karelerini temsil etmektedir.
Aşağıdaki ekran görüntüsü Derleyici Gezgini'nde çalışan programı göstermektedir:
Bu makalenin başında std::execution'ın üç temel soyutlaması olduğunu yazmıştım: zamanlayıcı, gönderen ve alıcı ve bir dizi özelleştirilebilir asenkron algoritma.
Yürütme kaynakları
Bu girişten sonra, özelleştirilebilir asenkron algoritma çeşitlerini daha ayrıntılı olarak tartışacağım ve başka örnekler sunacağım.
(Ben)
Bu yazı için planlarda bir değişiklik var. Asıl planım ana dilden sonra C++26 kütüphanesini tanıtmaktı. Ancak kütüphanenin uygulama durumu yeterince tamamlanmamıştır.
Duyuru
Rainer Grimm uzun yıllardır yazılım mimarı, ekip ve eğitim yöneticisi olarak çalışmaktadır. C++, Python ve Haskell programlama dilleri üzerine makaleler yazmaktan hoşlanıyor, aynı zamanda özel konferanslarda sık sık konuşmaktan da hoşlanıyor. Modern C++ adlı blogunda C++ tutkusunu yoğun bir şekilde ele alıyor.
Bu yüzden Eşzamanlılık e'yi kullanmaya karar verdim std::execution devam etmek. C++26'nın geri kalan özelliklerini derleyici uygulamaya koyar koymaz tanıtacağım.
Aufbau von std::execution
std::execution üç temel soyutlamaya sahiptir: zamanlayıcı, gönderen ve alıcı ve bir dizi özelleştirilebilir asenkron algoritma. Benim sunumum std::execution P2300R10 önerisine dayanmaktadır.
İlk deneyler
İlk deneylerimde stdexec kullandım. Nvidia'nın bu referans uygulaması, teklifin sekizinci revizyonuna dayanmaktadır. Bu deneyin kapsamını GitHub'da bulabilirsiniz:
- P2300'de önerilen tasarımın test uygulamasını sağlayın.
- Sender modeliyle deneme yapmak isteyen geliştiricilere erken erişim sağlayın.
- P2300'ün tasarımına katılmak veya katkıda bulunmak isteyenlerle işbirliği yapın (katkılar memnuniyetle karşılanır!).
#include <stdexec/execution.hpp>
#include <exec/static_thread_pool.hpp>
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` that creates a
// Sender that just forwards `n` to the next sender.
// After `just`, 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:rintf("%d %d %dn", i, j, k);
}
Bu programı revizyon 10 sözdizimine dönüştüreceğim. Program gerekli başlıkları ekleyerek başlıyor:exec/static_thread_pool.hpp> bir iş parçacığı havuzu oluşturmak için,stdexec/execution.hpp> yürütmeyle ilgili hizmetler için. içinde main-Fonksiyon aktif olacak static_thread_pool pool sekiz iş parçacığı ile oluşturulmuştur. İş parçacığı havuzu görevleri aynı anda yürütür. Bir zamanlayıcı nesnesi almak için iş parçacığı havuzu üye işlevi get_scheduler çağrılır sched elde etmek. Zamanlayıcı, iş parçacığı havuzundaki görevleri zamanlar.
Lambda işlevi fun bir tamsayı alır i girdi olarak ve karesini döndürür (i * i) Geriye doğru. Bu lambda sonraki görevlerde giriş değerlerine uygulanır. fonksiyon stdexec::when_all birden çok alt görevin tamamlanmasını bekleyen bir görev oluşturur. Her alt görev bu özellikle birlikte gelir stdexec::starts_on belirtilen zamanlayıcıda görevi oluşturur sched planlar. fonksiyon stdexec::just tek bir değer (0, 1 veya 2) üreten bir görev oluşturur ve işlev stdexec::then bunu yapmak için kullanılır fun-Bu değere lambda uygulayın. Ortaya çıkan görev nesnesi şöyle olur: work isminde.
fonksiyon stdexec::sync_wait daha sonra görevin tamamlanmasını eşzamanlı olarak beklemek için çağrılır. fonksiyon std::move işletmenin mülkiyetini devreder work İLE sync_wait. THE value-Üye işlevi sonuç için kullanılır sync_wait alt görevler tarafından oluşturulan değerleri almak için çağrılır. Bu değerler değişkenlerdedir i, Yung k ambalajından çıkarıldı.
Son olarak program aşağıdaki değerleri döndürür: i, j VE k ile std:rintf Konsolda. Bu değerler sırasıyla 0, 1 ve 2'nin karelerini temsil etmektedir.
Aşağıdaki ekran görüntüsü Derleyici Gezgini'nde çalışan programı göstermektedir:
Bu makalenin başında std::execution'ın üç temel soyutlaması olduğunu yazmıştım: zamanlayıcı, gönderen ve alıcı ve bir dizi özelleştirilebilir asenkron algoritma.
Yürütme kaynakları
- infaz yerini temsil eder e
- kodda herhangi bir temsile ihtiyaç duymazlar.
- yürütme kaynağını temsil eder.
- Zamanlayıcı kavramı tek gönderici algoritması ile tanımlanır: schedule.
- Algoritma schedule zamanlayıcı tarafından belirtilen yürütme kaynağı üzerinde çalışan bir göndereni döndürür.
- Bu vericiye bağlı bir alıcı nihayet bu değerleri aldığında bazı değerleri gönderir,
- just sözde verici fabrikasıdır.
- Üç kanalı destekler: değer, hata, iptal edildi.
- Kendisi sözde tüketici göndericisidir.
- İşi gönderin ve mevcut işi kilitleyin std::thread ve işin sonunda gönderen tarafından gönderilen isteğe bağlı bir değer kümesini döndürür.
Bu girişten sonra, özelleştirilebilir asenkron algoritma çeşitlerini daha ayrıntılı olarak tartışacağım ve başka örnekler sunacağım.
(Ben)