Repository navigation
threadsLinuxC
uper pytanie 👍
To jest dokładnie serce wątków w Linuksie.
Wyjaśnię każde pole bardzo prosto, po kolei.
Pełna deklaracja
int pthread_create(
pthread_t* thread,
const pthread_attr_t* attr,
void* (*start_routine)(void*),
void* arg
);
1️⃣ pthread_t* thread
👉 gdzie Linux zapisze ID wątku
pthread_t = „uchwyt / identyfikator wątku”
pthread_t* = wskaźnik, bo funkcja musi coś zapisać
📌 Po wywołaniu:
pthread_t t;
pthread_create(&t, ...);
➡️ t reprezentuje nowy wątek
Używany potem do:
pthread_join(t, ...);
pthread_kill(t, ...);
2️⃣ const pthread_attr_t* attr
👉 atrybuty wątku (opcjonalne)
Najczęściej:
nullptr
Ale można ustawić:
rozmiar stacka
priorytet
czy wątek jest detached
affinity CPU
Przykład:
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, 1024 * 1024);
📌 const → pthread_create tylko czyta
3️⃣ void* (*start_routine)(void*)
👉 funkcja, która ma się wykonać w nowym wątku
To jest wskaźnik na funkcję.
Czytamy to tak:
„wskaźnik na funkcję, która:
bierze void*
zwraca void*”
Przykład:
void* worker(void* arg);
➡️ Linux uruchomi:
worker(arg);
w nowym wątku
4️⃣ void* arg
👉 argument przekazany do funkcji wątku
przekazywany 1 do 1 do start_routine
może być:
nullptr
wskaźnik do struktury
wskaźnik do obiektu
wskaźnik do czegokolwiek
Przykład:
Data d{42};
pthread_create(&t, nullptr, worker, &d);
🔁 Jak to się łączy w całość?
pthread_create(&t, nullptr, worker, &d);
Linux robi logicznie:
// w nowym wątku:
worker(&d);
--------------------------------------------------------
Super — poniżej masz **kompletną, praktyczną ściągę pthreads** 📌
To jest **zestaw wszystkich najważniejszych funkcji (komend)**, **co robią** i **kiedy ich używać**.
Dokładnie to, czego się używa w realnym kodzie na Linuksie.
---
# 🧵 PTHREADS – KOMPLETNA ŚCIĄGA (Linux)
---
## 🔹 1️⃣ Tworzenie i kończenie wątku
### `pthread_create`
```c
int pthread_create(
pthread_t* thread,
const pthread_attr_t* attr,
void* (*start_routine)(void*),
void* arg
);
➡️ tworzy i uruchamia wątek
➡️ start_routine(arg) zaczyna się wykonywać natychmiast
void pthread_exit(void* retval);➡️ kończy bieżący wątek
➡️ zwraca wartość do pthread_join
int pthread_join(pthread_t thread, void** retval);➡️ czeka aż wątek się zakończy ➡️ zwalnia zasoby wątku
int pthread_detach(pthread_t thread);➡️ ustawia wątek jako samoczyszczący się ➡️ NIE czekasz na zakończenie
pthread_t pthread_self(void);➡️ zwraca ID bieżącego wątku (pthread)
int pthread_equal(pthread_t t1, pthread_t t2);➡️ porównuje dwa pthread_t
pthread_attr_init(&attr);
pthread_attr_destroy(&attr);pthread_attr_setstacksize(&attr, size_t size);➡️ ustawia rozmiar stacka wątku
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);➡️ wątek od razu detached
pthread_mutex_init(&m, NULL);
pthread_mutex_destroy(&m);pthread_mutex_lock(&m);
pthread_mutex_unlock(&m);➡️ ochrona sekcji krytycznej
pthread_mutex_trylock(&m);➡️ próbuje zablokować (nie czeka)
pthread_cond_init(&cv, NULL);
pthread_cond_destroy(&cv);pthread_cond_wait(&cv, &mutex);➡️ zwalnia mutex ➡️ usypia wątek ➡️ po sygnale blokuje mutex z powrotem
pthread_cond_signal(&cv);➡️ budzi jeden wątek
pthread_cond_broadcast(&cv);➡️ budzi wszystkie czekające wątki
pthread_barrier_init(&b, NULL, N);
pthread_barrier_wait(&b);➡️ czeka aż N wątków dotrze do punktu
pthread_rwlock_rdlock(&rw);
pthread_rwlock_wrlock(&rw);➡️ wielu czytelników / jeden pisarz
pthread_key_create(&key, destructor);pthread_setspecific(key, value);
pthread_getspecific(key);➡️ dane prywatne dla wątku
pthread_kill(thread, SIGUSR1);➡️ wysyła sygnał do konkretnego wątku
pthread_cancel(thread);➡️ próbuje anulować wątek ❌ ryzykowne
pthread_testcancel();➡️ punkt anulowania
pthread_setschedparam(thread, SCHED_FIFO, ¶m);➡️ ustawia politykę schedulera
pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset);➡️ przypisuje wątek do CPU
syscall(SYS_gettid);ps -T -p PID
htop # Show threads- ✔ create → run → exit
- ✔ join ALBO detach
- ❌ brak join/detach = leak
- ❌ brak mutexa = race
- pthreads to niski poziom
- wszystko jest jawne
- kernel widzi wątki jako taski
- thread pool > milion wątków
Jeśli chcesz, następnym krokiem możemy:
- napisać pełny thread pool
- zrobić ściągę tylko mutex/cond
- przeanalizować deadlock krok po kroku
- porównać pthread vs C++ std::thread
Jasne 👍
Zrobimy kilka bardzo prostych, „życiowych” przykładów użycia danych w wątkach pthread, tak żebyś zobaczył różne wzorce, a nie tylko jeden.
Każdy przykład = konkretny cel + minimalny kod + co z tego wynika.
Każdy wątek liczy coś niezależnie.
#include <pthread.h>
#include <iostream>
struct Args {
int id;
};
void* worker(void* arg) {
Args* a = (Args*)arg;
std::cout << "Wątek " << a->id << "\n";
return nullptr;
}
int main() {
pthread_t t[3];
Args args[3];
for (int i = 0; i < 3; i++) {
args[i].id = i;
pthread_create(&t[i], nullptr, worker, &args[i]);
}
for (int i = 0; i < 3; i++)
pthread_join(t[i], nullptr);
}
✔ dane są na stacku main
✔ każdy wątek ma własne Args
✔ brak współdzielenia → brak mutexa
Kilka wątków modyfikuje jedną wspólną zmienną.
#include <pthread.h>
#include <iostream>
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
struct Shared {
int counter;
};
void* worker(void* arg) {
Shared* s = (Shared*)arg;
pthread_mutex_lock(&m);
s->counter++;
pthread_mutex_unlock(&m);
return nullptr;
}
int main() {
Shared s{0};
pthread_t t[5];
for (int i = 0; i < 5; i++)
pthread_create(&t[i], nullptr, worker, &s);
for (int i = 0; i < 5; i++)
pthread_join(t[i], nullptr);
std::cout << "counter = " << s.counter << "\n";
}
❌ bez mutexa → zły wynik
✔ mutex chroni wspólne dane
Wspólna struktura na heapie (typowe w systemach).
#include <pthread.h>
#include <iostream>
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
struct Shared {
int value;
};
void* worker(void* arg) {
Shared* s = (Shared*)arg;
pthread_mutex_lock(&m);
s->value += 10;
pthread_mutex_unlock(&m);
return nullptr;
}
int main() {
Shared* s = new Shared{0};
pthread_t t1, t2;
pthread_create(&t1, nullptr, worker, s);
pthread_create(&t2, nullptr, worker, s);
pthread_join(t1, nullptr);
pthread_join(t2, nullptr);
std::cout << s->value << "\n";
delete s;
}
Jeden wątek produkuje dane, drugi je odbiera.
#include <pthread.h>
#include <iostream>
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
int data = 0;
bool ready = false;
void* producer(void*) {
pthread_mutex_lock(&m);
data = 42;
ready = true;
pthread_cond_signal(&cv);
pthread_mutex_unlock(&m);
return nullptr;
}
void* consumer(void*) {
pthread_mutex_lock(&m);
while (!ready)
pthread_cond_wait(&cv, &m);
std::cout << "Odebrane: " << data << "\n";
pthread_mutex_unlock(&m);
return nullptr;
}
int main() {
pthread_t p, c;
pthread_create(&c, nullptr, consumer, nullptr);
pthread_create(&p, nullptr, producer, nullptr);
pthread_join(p, nullptr);
pthread_join(c, nullptr);
}
Wątek liczy wynik i oddaje go.
#include <pthread.h>
#include <iostream>
void* worker(void*) {
int* result = new int(123);
return result;
}
int main() {
pthread_t t;
void* ret;
pthread_create(&t, nullptr, worker, nullptr);
pthread_join(t, &ret);
int* value = (int*)ret;
std::cout << *value << "\n";
delete value;
}
| Sytuacja | Wzorzec |
|---|---|
| Brak współdzielenia | osobne dane |
| Wspólna zmienna | mutex |
| Wiele zadań | thread pool |
| Kolejka | cond var |
| Wynik z wątku | join + return |
✔ wątki mogą używać danych na stacku
main✔ heap jest wspólny
✔ mutex chroni dane
❌ brak synchronizacji = UB
❌
deletew wątku bez umowy = crash
Jeśli chcesz, następne dobre kroki:
pełny producer–consumer (queue)
thread pool na pthread
typowe bugi (race, deadlock)
jak Linux planuje te wątki
Napisz, co robimy dalej 👌