Eşzamansız Programlama – Bölüm 1: C++ Boost.Asio ile Kolayca

Boost.Asio ile C++'da eşzamansız programlama için iyi bilinen ancak modern, verimli ve güçlü bir kitaplığa sahip olursunuz. C++ sürümüne bağlı olarak geliştiriciler, karmaşık ve hataya açık iş parçacığı mekanizmalarına başvurmak zorunda kalmadan farklı şekillerde eşzamansız olarak programlayabilirler.

Martin Meeser bağımsız bir bilgisayar bilimcisidir (üniversite) ve yazılım geliştirme alanında hizmetler sunmaktadır: bireysel yazılım geliştirme, süreç danışmanlığı ve eğitim. Otomotiv, finans, uzay yolculuğu, radyo astronomi ve tıbbi teknoloji sektörleri de dahil olmak üzere çok sayıda projede müşterilerine destek vermiştir.

Bu makale, yakın zamanda tanıtılan Python dışında çeşitli dillerde eşzamansız programlamaya ilişkin bir serinin ilkidir.

Linux sistemlerinde Boost'un güncel sürümünü çeşitli paket yöneticileri aracılığıyla edinebilirsiniz; örneğin Ubuntu'da yetenekle şu anda sürüm 1.83'tür:

sudo apt-get install libboost-all-dev

En son sürümü (şu anda 1.89) kullanmak veya Windows altında çalışmak istiyorsanız ana sayfadan Boost'u indirin ve kendiniz oluşturun. İlgili talimatlar tedarikçide bulunabilir. Ayrıca Boost bağımlılıkları olmayan ve farklı bir ad alanına sahip, aynı, saf bir Asio kütüphanesi de vardır, ancak bu burada dikkate alınmamıştır.

Temel olarak, eşzamansız programlama, API'yi ve mekanizmaları sağlayan ve programın akışını düzenleyen bir yönetim sistemi gerektirir. Burada “olay döngüsü” terimi genel olarak kendini kanıtladı, ancak Boost.Asio “bağlam”dan bahsediyor. Ortak yaklaşım bir taneye sahip olmaktır. io_context aşağıdaki örnekte gösterildiği gibi kullanmak için:


#include <boost/asio.hpp>
#include <iostream>

int main()
{
    boost::asio::io_context io_context;
    boost::asio::post(io_context, []()
    {
        std::cout << "Hello World from async context!" << std::endl;
    });

    // async execution startet nach dem Aufruf von io_context.run()
    io_context.run(); // run() blockiert bis keine Arbeit mehr vorliegt
}

Liste 1: Bağlam kullanmanın basit örneği

post() Parametre olarak iletilen işlev nesnesini (burada bir lambda) bağlam kuyruğuna ekler ve hemen geri döner.

Bağlam çağrılmasıyla başlar io_context.run() ve şimdi fonksiyonları birbiri ardına işlemeye başlayın. Örnekte “Merhaba Dünya…” yalnızca sonradan görünüyor io_context.run() piyasaya sürülmüş.

Bu, tam olarak başlatılmamış bir sistemi etkileyen eşzamansız mekanizmalar olmadan programı başlatır. Bu, başka bir işlemden gelen rastgele mesajlar veya başka bir programın yüksek CPU kullanımı gibi belirli koşullar altında ortaya çıkan ve bulunması zor olan hataları önler.

Yöntem io_context.run() başka görev kalmadığı anda sona erer veya io_context.stop() denir. io_context.run() iş parçacığı başına yalnızca bir kez kullanılabilir. Ancak mümkün run() aynısıyla io_context birden fazla iş parçacığından.

Liste 1 durumunda, io_context tek bir iş parçacığının çalışması, yani eşzamanlı yürütme anlamına gelir. Liste 2'de io_context.run() birden fazla iş parçacığı tarafından çağrılır. Şimdi görevleri bağlama aktarırsanız, iş parçacıklarından birinde görevler hemen yürütülür. Bu yürütme gerçekten paraleldir. Tüm iş parçacıkları zaten işle meşgulse yeni görev sıraya alınır. İş parçacıklarından biri görevini tamamladıktan sonra Context, iş parçacığını kuyruktan atar.


#include <boost/asio.hpp>
#include <iostream>
#include <thread>
#include <vector>

int main()
{
    boost::asio::io_context io_context;

    int n = 4;
    std::vector<std::thread> threads(n);
    for (int i = 0; i < n; i++)
    {
        threads[i] = std::thread([&io_context](){ io_context.run();});
    }

    // ... parallel asynchrone Aktionen hier

    for (auto& t : threads)
    {
        if (t.joinable()){ t.join(); }
    }
}

Liste 2: Bir io_context'te birden fazla iş parçacığı kullanma

Burada asenkron programlama stilinin gücünü zaten hayal edebilirsiniz: yürütme birimlerinin yerini somut bir birim alıyor Thread. Eşzamansız programlar yerel olarak ölçeklenir ve mevcut tüm kaynakları kullanır.

Deneyimlerime göre genellikle bir tane var Thread tamamen yeterli olmakla birlikte birkaç Threads özellikle sunucu sistemleri veya çok özel görevler (örneğin büyük asal sayıların hesaplanması veya diğer algoritmik problemler) için ilgi çekicidirler. Bağlamda ne kadar çok iş parçacığı (veya CPU çekirdeği) mevcutsa, programda herhangi bir değişiklik yapılmadan paralel olarak o kadar kısmi hesaplama gerçekleştirilebilir. Geliştiriciler tamamen işlevselliğe odaklanabilirler.

davranışını değiştirebilirsiniz io_context.run() birer birer değiştir work_guard Liste 3'te gösterildiği gibi kullanılır.


#include <boost/asio.hpp>
#include <iostream>

int main()
{
    boost::asio::io_context io_context;
    boost::asio::executor_work_guard<boost::asio::io_context::executor_type>
    work_guard(io_context.get_executor());

    boost::asio::post(io_context, []()
    {
        std::cout << "Hello World from async context." << std::endl;
    });

    boost::asio::signal_set signals(io_context, SIGINT, SIGTERM);
    signals.async_wait([&io_context /*&work_guard*/]
        (const boost::system::error_code& ec, int signal)
        {
            io_context.stop(); // bricht sofort alles ab
            // work_guard.reset(); // bricht ab, wenn alles fertig ist
        });

    io_context.run();
}

Liste 3: executor_work_guard, bağlamın işi bittiğinde run() işlevinin geri alınmasını önleyebilir.

Şimdi atla io_context.run() ancak bir tarafta geri döndüğümde io_context.stop() çağrıldı: Bu durumda, önceden planlanmış görevler artık yürütülmez ve halihazırda çalışmakta olan görevler bir durdurma hatasıyla durdurulur. Öte yandan, fonksiyon şu durumlarda döner: work_guard.reset() çağrıldıktan sonra program, o anda çalışan tüm görevleri ve zamanlanmış tüm görevleri işlemeye devam eder.

İkinci durumda, geliştiriciler sıraya alınmış birimlerin içine yeni görevler eklememeye dikkat etmelidir, aksi takdirde kod sonsuz bir eşzamansız döngüye girer. io_context.run() asla bitmeyecek.

Boost.Asio'da iki bağlam daha vardır: thread_pool (bkz. Liste 4) e system_context (bkz. Liste 5).


#include <boost/asio.hpp>
#include <iostream>

int main()
{
    boost::asio::thread_pool thread_pool(16);
    boost::asio::post(thread_pool, []()
    {
        std::cout << "Hello World from async context!" << std::endl;
    });

    // thread_pool.stop(); // bricht alle Arbeit im Pool sofort ab
    thread_pool.join(); // wartet bis alle Arbeit abgeschlossen ist

Liste 4: thread_pool kullanımına örnek

THE thread_pool geliştiriciler birden fazla iş parçacığı kullanmak istediklerinde ve her iş parçacığının ayrıntılı kontrolüne ihtiyaç duymadıklarında basit ve önerilen bir alternatiftir. Ancak bir şeyi göz önünde bulundurmak gerekiyor: farklı io_context başlangıç thread_pool inşaattan hemen sonra. Aksi takdirde benzer şekilde davranır io_context: Yöntem ile thread_pool.stop() geliştiriciler yürütmeyi derhal sonlandırır thread_pool.join() Ancak program yine de tüm görevleri tamamlıyor. Daha önceydi stop() çağırdım, sonra atladım join() hemen geri dönün.

THE system_context Her zaman her yerden ulaşılabilir; başlatmanıza veya durdurmanıza gerek yok. Geliştiricilerin bir noktada çok fazla çaba harcamadan ve hazırlık yapmadan, aynı zamanda kendi kontrolleri olmadan eşzamansız işlemler gerçekleştirmek istemeleri durumunda bu iyi bir seçimdir.


#include <boost/asio.hpp>
#include <thread>

int main()
{
    boost::asio::system_context system_context;
    boost::asio::post(system_context, []()
    {
        std::cout << "Hello World from async context!" << std::endl;
    });
}

Liste 5: system_context kullanımına örnek


Yayımlandı

kategorisi

yazarı:

Etiketler:

Yorumlar

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir