concurrency (tsz. concurrencies)
A konkurencia (concurrency) a számítástechnikában azt jelenti, hogy egy program képes egyszerre több műveletet is futtatni, akár párhuzamosan, akár egy időosztásos módon. A C++ nyelv modern szabványai (főként C++11-től kezdődően) beépített támogatást nyújtanak a konkurens programozáshoz, többek között a std::thread
, std::mutex
, std::async
, std::future
, és std::atomic
eszközök révén.
Fogalom | Jelentés |
---|---|
Konkurencia | Több feladat kezelése egyszerre, de nem feltétlenül párhuzamosan (lehet, hogy csak időosztással) |
Párhuzamosság | Több feladat valódi egyidejű végrehajtása, például több CPU magon |
A szál egy végrehajtási egység egy folyamaton belül. Egy folyamat több szálat tartalmazhat, amelyek osztoznak az adatokon és a memórián.
C++11-től kezdve a nyelv szabványos könyvtára tartalmazza a <thread>
fejléccel elérhető std::thread
osztályt.
#include <iostream>
#include <thread>
using namespace std;
void hello() {
cout << "Szia a második szálból!\n";
}
int main() {
thread t(hello); // új szál indítása
cout << "Szia a fő szálból!\n";
t.join(); // várunk, míg a szál befejeződik
return 0;
}
join()
– fő szál megvárja a mellékszál végétdetach()
– a szál háttérben fut, önállóanjoinable()
– ellenőrizhető, hogy egy szál csatlakoztatható-e
Ha két szál ugyanazt az erőforrást (változót, memóriát stb.) akarja használni, versenyhelyzet alakulhat ki.
#include <mutex>
mutex mtx;
void kritikus_szakasz() {
mtx.lock();
// kritikus rész: csak egy szál férhet hozzá
mtx.unlock();
}
💡 Javasolt forma: lock_guard
void biztonsagos_funkcio() {
lock_guard<mutex> l(mtx); // automatikus unlock a scope végén
// biztonságos kód
}
Ez akkor fordul elő, amikor két vagy több szál egyszerre próbálja módosítani ugyanazt az adatot, és a végeredmény nem determinisztikus.
std::atomic
Az atomi típusok biztosítják, hogy egy változóval való minden művelet oszthatatlanul hajtódjon végre.
#include <atomic>
atomic<int> szamlalo(0);
void novel() {
szamlalo++;
}
std::async
, std::future
– késleltetett végrehajtás#include <future>
int hosszadalmasMuvelet() {
this_thread::sleep_for(chrono::seconds(2));
return 42;
}
int main() {
future<int> eredmeny = async(hosszadalmasMuvelet);
cout << "Várakozás...\n";
cout << "Eredmény: " << eredmeny.get(); // blokkolás a végeredményig
}
promise
és future
– kézi eredmény továbbítás#include <future>
void producer(promise<int>&& p) {
p.set_value(100);
}
int main() {
promise<int> p;
future<int> f = p.get_future();
thread t(producer, move(p));
cout << "Kapott érték: " << f.get();
t.join();
}
Eszköz | Leírás |
---|---|
condition_variable
|
Értesítés eseményekről |
shared_mutex
|
Több olvasó, egy író |
barrier , latch
|
Szálak összehangolása (C++20) |
semaphore
|
Erőforrás hozzáférés korlátozása (C++20) |
Hiba | Leírás |
---|---|
Deadlock | Két szál kölcsönösen vár egymásra |
Race condition | Véletlenszerű viselkedés nem szinkronizált hozzáférés miatt |
Detached szál elhal | detach() után a szál hibát okozhat, ha nem fut tovább vagy eléri a hibás állapotot
|
Elfelejtett join()
|
A fő szál befejeződhet a többi előtt, crash |
#include <thread>
#include <vector>
#include <iostream>
using namespace std;
void szamolj(int tol, int ig, int& osszeg, mutex& m) {
int resz = 0;
for (int i = tol; i <= ig; ++i)
resz += i;
lock_guard<mutex> l(m);
osszeg += resz;
}
int main() {
mutex m;
int osszeg = 0;
thread t1(szamolj, 1, 50, ref(osszeg), ref(m));
thread t2(szamolj, 51, 100, ref(osszeg), ref(m));
t1.join();
t2.join();
cout << "Összeg: " << osszeg << endl;
}
Fogalom | Példa |
---|---|
Szál indítása | std::thread t(f);
|
Szinkronizáció | std::mutex , lock_guard
|
Atomi változó | std::atomic<int>
|
Async futtatás | std::async , std::future
|
Értesítések | std::condition_variable
|
<execution>
- C++17)