Ugrás a tartalomhoz

std::thread

A Wikiszótárból, a nyitott szótárból


Főnév

std::thread (tsz. std::threads)

  1. (informatika) Az std::thread a C++ szabványos könyvtárának része, amely lehetővé teszi a többszálú programozást (multithreading). A többszálúság azt jelenti, hogy egy program egyszerre több szálon (thread) is futhat, ami párhuzamos végrehajtást és teljesítményjavulást eredményezhet.

Ebben a részletes útmutatóban bemutatjuk az std::thread használatát, az alapvető műveleteket, a szinkronizációs eszközöket és néhány gyakori hibát.



1. std::thread alapjai

A std::thread segítségével egy új végrehajtási szálat hozhatunk létre. A szál egy függvény végrehajtását végzi el, amely lehet: - Egy normál függvény - Egy lambda kifejezés - Egy osztály tagfüggvénye

Egyszerű std::thread példa

#include <iostream>
#include <thread>

void szamolas() {
    for (int i = 0; i < 5; i++) {
        std::cout << "Számolás: " << i << std::endl;
    }
}

int main() {
    std::thread szal(szamolas); // Létrehozunk egy szálat
    szal.join(); // Megvárjuk a szál végét
    std::cout << "Főprogram vége." << std::endl;
    return 0;
}

Magyarázat:

  • std::thread szal(szamolas); → Elindít egy új szálat, amely a szamolas függvényt futtatja.
  • szal.join(); → A főprogram megvárja, hogy a szál befejezze a végrehajtást.



2. Több szál futtatása

Ha több szálat szeretnénk egyidejűleg elindítani, minden szálhoz külön std::thread példányt hozunk létre.

Több szál futtatása

#include <iostream>
#include <thread>

void munka(int id) {
    for (int i = 0; i < 5; i++) {
        std::cout << "Szál " << id << ": " << i << std::endl;
    }
}

int main() {
    std::thread szal1(munka, 1);
    std::thread szal2(munka, 2);

    szal1.join();
    szal2.join();

    std::cout << "Főprogram vége." << std::endl;
    return 0;
}

Itt két különálló szál fut párhuzamosan, mindkettő a munka függvényt végzi el.



3. Szál kezelése – join() és detach()

Az std::thread objektumokat megfelelően kell kezelni, különben a program összeomolhat.

join() – Megvárja a szál végét

A join() segítségével a főprogram várakozik, amíg a szál befejezi a futását.

std::thread szal(munka);
szal.join(); // Megvárjuk, hogy a szál befejezze a munkáját

Ha nem hívjuk meg a join() függvényt, a program összeomolhat, mert a szál objektum megsemmisülne, miközben még fut.

detach() – Leválasztja a szálat

A detach() segítségével a szál függetlenül fut tovább, és a főprogram nem vár rá.

std::thread szal(munka);
szal.detach(); // A szál függetlenül fut tovább

A detach() használatakor biztosítani kell, hogy a szál hozzáférhető maradjon a memória felszabadításáig, különben undefined behavior léphet fel.



4. Szinkronizáció és versenyhelyzetek

Ha több szál módosítja ugyanazt az adatot, versenyhelyzetek alakulhatnak ki.

Versenyhelyzet példája

#include <iostream>
#include <thread>

int szamlalo = 0;

void novel() {
    for (int i = 0; i < 1000; i++) {
        szamlalo++; // Versenyhelyzet
    }
}

int main() {
    std::thread szal1(novel);
    std::thread szal2(novel);

    szal1.join();
    szal2.join();

    std::cout << "Számláló értéke: " << szamlalo << std::endl; // Nem biztos, hogy 2000 lesz!
    return 0;
}

A program nem garantálja, hogy a szamlalo értéke 2000 lesz, mert a két szál egyszerre próbálja módosítani.



5. Mutex – Kölcsönös kizárás

A versenyhelyzetek elkerülésére használhatunk std::mutex-et.

#include <iostream>
#include <thread>
#include <mutex>

int szamlalo = 0;
std::mutex mtx;

void novel() {
    for (int i = 0; i < 1000; i++) {
        std::lock_guard<std::mutex> lock(mtx); // Biztosítja, hogy egyszerre csak egy szál írhatja
        szamlalo++;
    }
}

int main() {
    std::thread szal1(novel);
    std::thread szal2(novel);

    szal1.join();
    szal2.join();

    std::cout << "Számláló értéke: " << szamlalo << std::endl; // Most garantáltan 2000
    return 0;
}

Itt a mutex (mtx) biztosítja, hogy egyszerre csak egy szál módosíthatja a változót.



6. Deadlock és megoldása

Ha több mutexet használunk, előfordulhat holtverseny (deadlock).

Deadlock példa

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx1, mtx2;

void f1() {
    mtx1.lock();
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
    mtx2.lock(); // Deadlock lehetséges
    std::cout << "f1 fut" << std::endl;
    mtx2.unlock();
    mtx1.unlock();
}

void f2() {
    mtx2.lock();
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
    mtx1.lock(); // Deadlock lehetséges
    std::cout << "f2 fut" << std::endl;
    mtx1.unlock();
    mtx2.unlock();
}

int main() {
    std::thread t1(f1);
    std::thread t2(f2);

    t1.join();
    t2.join();
    return 0;
}

A megoldás az std::lock(), amely egyszerre próbálja lezárni a mutexeket.

std::lock(mtx1, mtx2);

7. Condition Variables (Feltételes változók)

A std::condition_variable lehetővé teszi, hogy egy szál várjon egy eseményre.

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

std::mutex mtx;
std::condition_variable cv;
bool kesz = false;

void dolgozo() {
    std::unique_lock<std::mutex> lock(mtx);
    cv.wait(lock, [] { return kesz; });
    std::cout << "Munka elindult!" << std::endl;
}

void vezerlo() {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    {
        std::lock_guard<std::mutex> lock(mtx);
        kesz = true;
    }
    cv.notify_one();
}

int main() {
    std::thread t1(dolgozo);
    std::thread t2(vezerlo);

    t1.join();
    t2.join();

    return 0;
}

Összegzés

Az std::thread lehetővé teszi több szál egyidejű futtatását, de figyelni kell a szinkronizációra és a versenyhelyzetekre. Az olyan eszközök, mint a mutex és a condition variable, segítenek a stabil programok megírásában.