Skip to content

threadsLinuxC

Stas edited this page Dec 23, 2025 · 5 revisions
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


pthread_exit

void pthread_exit(void* retval);

➡️ kończy bieżący wątek ➡️ zwraca wartość do pthread_join


pthread_join

int pthread_join(pthread_t thread, void** retval);

➡️ czeka aż wątek się zakończy ➡️ zwalnia zasoby wątku


pthread_detach

int pthread_detach(pthread_t thread);

➡️ ustawia wątek jako samoczyszczący się ➡️ NIE czekasz na zakończenie


pthread_self

pthread_t pthread_self(void);

➡️ zwraca ID bieżącego wątku (pthread)


pthread_equal

int pthread_equal(pthread_t t1, pthread_t t2);

➡️ porównuje dwa pthread_t


🔹 2️⃣ Atrybuty wątku (pthread_attr_*)

pthread_attr_init / destroy

pthread_attr_init(&attr);
pthread_attr_destroy(&attr);

pthread_attr_setstacksize

pthread_attr_setstacksize(&attr, size_t size);

➡️ ustawia rozmiar stacka wątku


pthread_attr_setdetachstate

pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

➡️ wątek od razu detached


🔹 3️⃣ Mutex (blokady)

pthread_mutex_init / destroy

pthread_mutex_init(&m, NULL);
pthread_mutex_destroy(&m);

pthread_mutex_lock / unlock

pthread_mutex_lock(&m);
pthread_mutex_unlock(&m);

➡️ ochrona sekcji krytycznej


pthread_mutex_trylock

pthread_mutex_trylock(&m);

➡️ próbuje zablokować (nie czeka)


🔹 4️⃣ Condition Variable (czekanie)

pthread_cond_init / destroy

pthread_cond_init(&cv, NULL);
pthread_cond_destroy(&cv);

pthread_cond_wait

pthread_cond_wait(&cv, &mutex);

➡️ zwalnia mutex ➡️ usypia wątek ➡️ po sygnale blokuje mutex z powrotem


pthread_cond_signal

pthread_cond_signal(&cv);

➡️ budzi jeden wątek


pthread_cond_broadcast

pthread_cond_broadcast(&cv);

➡️ budzi wszystkie czekające wątki


🔹 5️⃣ Synchronizacja niskopoziomowa

pthread_barrier_*

pthread_barrier_init(&b, NULL, N);
pthread_barrier_wait(&b);

➡️ czeka aż N wątków dotrze do punktu


pthread_rwlock_*

pthread_rwlock_rdlock(&rw);
pthread_rwlock_wrlock(&rw);

➡️ wielu czytelników / jeden pisarz


🔹 6️⃣ Thread Local Storage (TLS)

pthread_key_create

pthread_key_create(&key, destructor);

pthread_setspecific / getspecific

pthread_setspecific(key, value);
pthread_getspecific(key);

➡️ dane prywatne dla wątku


🔹 7️⃣ Sygnały i kontrola

pthread_kill

pthread_kill(thread, SIGUSR1);

➡️ wysyła sygnał do konkretnego wątku


pthread_cancel ⚠️

pthread_cancel(thread);

➡️ próbuje anulować wątek ❌ ryzykowne


pthread_testcancel

pthread_testcancel();

➡️ punkt anulowania


🔹 8️⃣ Planowanie (zaawansowane)

pthread_setschedparam

pthread_setschedparam(thread, SCHED_FIFO, &param);

➡️ ustawia politykę schedulera


pthread_setaffinity_np

pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset);

➡️ przypisuje wątek do CPU


🔹 9️⃣ Debug / info

Kernel TID

syscall(SYS_gettid);

CLI

ps -T -p PID
htop   # Show threads

🧠 ZŁOTA ZASADA pthread

  • ✔ create → run → exit
  • ✔ join ALBO detach
  • ❌ brak join/detach = leak
  • ❌ brak mutexa = race

TL;DR

  • 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

Powiedz 👍

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.


🧵 PRZYKŁADY UŻYCIA DANYCH W WĄTKACH (pthread)


🟢 1️⃣ Każdy wątek dostaje WŁASNE dane (bez mutexa)

Cel

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 &lt; 3; i++) {
    args[i].id = i;
    pthread_create(&amp;t[i], nullptr, worker, &amp;args[i]);
}

for (int i = 0; i &lt; 3; i++)
    pthread_join(t[i], nullptr);

}

Co tu widzisz?

✔ dane są na stacku main
✔ każdy wątek ma własne Args
✔ brak współdzielenia → brak mutexa


🟢 2️⃣ Wspólne dane + mutex (klasyka)

Cel

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(&amp;m);
s-&gt;counter++;
pthread_mutex_unlock(&amp;m);

return nullptr;

}

int main() { Shared s{0}; pthread_t t[5];

for (int i = 0; i &lt; 5; i++)
    pthread_create(&amp;t[i], nullptr, worker, &amp;s);

for (int i = 0; i &lt; 5; i++)
    pthread_join(t[i], nullptr);

std::cout &lt;&lt; "counter = " &lt;&lt; s.counter &lt;&lt; "\n";

}

Wniosek

❌ bez mutexa → zły wynik
✔ mutex chroni wspólne dane


🟢 3️⃣ Dane na HEAPIE, używane przez wątki

Cel

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(&amp;m);
s-&gt;value += 10;
pthread_mutex_unlock(&amp;m);

return nullptr;

}

int main() { Shared* s = new Shared{0}; pthread_t t1, t2;

pthread_create(&amp;t1, nullptr, worker, s);
pthread_create(&amp;t2, nullptr, worker, s);

pthread_join(t1, nullptr);
pthread_join(t2, nullptr);

std::cout &lt;&lt; s-&gt;value &lt;&lt; "\n";
delete s;

}


🟢 4️⃣ Producer → Consumer (cond var, minimum)

Cel

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 &lt;&lt; "Odebrane: " &lt;&lt; data &lt;&lt; "\n";
pthread_mutex_unlock(&amp;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);

}


🟢 5️⃣ Przekazywanie wyniku z wątku (pthread_join)

Cel

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(&amp;t, nullptr, worker, nullptr);
pthread_join(t, &amp;ret);

int* value = (int*)ret;
std::cout &lt;&lt; *value &lt;&lt; "\n";
delete value;

}

⚠️ heap → main sprząta


🧠 JAK WYBRAĆ WZORZEC?

Sytuacja Wzorzec
Brak współdzielenia osobne dane
Wspólna zmienna mutex
Wiele zadań thread pool
Kolejka cond var
Wynik z wątku join + return

TL;DR

  • ✔ wątki mogą używać danych na stacku main

  • ✔ heap jest wspólny

  • ✔ mutex chroni dane

  • ❌ brak synchronizacji = UB

  • ❌ delete w 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 👌

Clone this wiki locally