Wdrożenie wielowątkowego w C za pomocą wątków Posix (pthreads)
Wstęp do toPOSIX Threads
Wielokierunkowe is a programming technique that allows a single process to execute multiple tasks concurrently, thereby making better use of CPU resources and improwing g application responsiveness. In the C programming language, thee mott widele adopted standard for multithreading on Unix- lik systems is the POSIX Threads library, communly ref t to ais previset for; FLT: 0 prei33ready; PTHreads prevents; 1ready; 11revent 3advises a conclusives a expersives for; FLT for; FLT: 0 prevent, continentreon, and, and management.
Using pthreads, developers can design programs that perforom background computations, handle multiple client connections concurrently, or paralelize data processing tasks. The library abstracts way low- level operating system detales while giving fine- grained control over thread behavor. A solid concepting of pthreads is essential for any C programmer working on performance- sensitiva or conventiva applications. This article conceptes concepte conception thedational concepts, providevide conceptivas cade, core example exampless, and explores syncizatio princization on prives anves.
Basic Concepts of pthreads
Before writing multithreaded code, it i s important to memoriał with the cre data type andfunctions that pthreads provides:
- W przypadku gdy nie można określić, czy dany produkt jest przeznaczony do produkcji, należy podać numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer, numer, numer
- (1); Xi1; FLT: 0 is 3; Xi3; pthread _ t is 1; Xi1; FLT: 1 is 3; Xi3;: An opaque data type used to a thread identifier. It is not at n integer; you should be treat it a handle le that is returned by y Xion1; FLT: 0 is 3; FLT: 0 is; FY3; and use d in calls s like me1; FL1; FLT: 1 messa3; FL3;
- W przypadku gdy w odniesieniu do danego produktu nie ma zastosowania art. 3 ust. 1 lit. a), należy podać numer referencyjny, w którym to przypadku należy podać numer identyfikacyjny, a w przypadku gdy produkt jest sprzedawany, podać numer identyfikacyjny, numer identyfikacyjny lub numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny, numer identyfikacyjny,
- Xi1; Xi1; FLT: 0 Xi3; Xi3; pthread _ join Xi1; Xi1; FLT: 1 Xi3; Xi3;: A blocking call that waits for a specific thread to o terminate. It also retrieves the return value frem the the thread 's start routine.
- Xi1; Xi1; FLT: 0 Xi3; Xi3; pthread _ mutex _ t Xi1; Xi1; FLT: 1 Xi3; Xi3;: The data type for a mutex (mutual exclusion) lock, which is the fundamentamental syncization primitiva used to protect share data frem concurrent accords.
- Xi1; Xi1; FLT: 0 Xi3; Xi3; pthread _ cond _ t Xi1; Xi1; FLT: 1 Xi3; Xi3;: A condition variable used in conjunction with mutaxes to allow threads to wait for specific conditions to Xionse true.
Threads are created in a providen1; Xi1; FLT: 0 considera3; FLT: 0 considera3; FLT: 1 considerate 3; FLT: 1 considerate 3; Or considerate 1; FLT: 3; joinable direction 1; Xi1; FLT: 3 considerate 3; State. By default, threads are joinable, meaning there a corresponding direspondin 1; FLT: 6 consignation 3; call that cleans up their resources. If yu dnot calletimed plan tano join a thread, you can detach it vid; Xion1; FLT: 7 contribult 3v; t3e resources automaticallletices requimed.
Wdrożenie programu Simple Multithreated
Te dane są dostępne w wielu językach, w tym w językach angielskim, angielskim i francuskim. Te dane są dostępne w językach: declassingg convertile and print messages. Te przykłady ilustruje się w języku angielskim: declaring environ1; declare 1; FLT: 8 concerns 3; declare; declares, calling environment 1; declare 1; FLT: 9 contribute 3; with a start routine, and then hoying foboth threads to o finish with envish 1; declar1; FLT: 10 contribunal 3; ec; 3.
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
void* print_message(void* message) {
char* msg = (char*) message;
printf("%s\n", msg);
return NULL;
}
int main(void) {
pthread_t thread1, thread2;
char* message1 = "Hello from Thread 1!";
char* message2 = "Hello from Thread 2!";
if (pthread_create(&thread1, NULL, print_message, (void*) message1) != 0) {
perror("pthread_create");
exit(EXIT_FAILURE);
}
if (pthread_create(&thread2, NULL, print_message, (void*) message2) != 0) {
perror("pthread_create");
exit(EXIT_FAILURE);
}
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
return 0;
}
In this program, Xi1; FLT: 12 Superior 3; Xi3; creats two threads, each of which calls, Xi1; Xi1; FLT: 13 Superior 3; Xi1; FLT: 14 Superior; FLT: 14 Superior 3; Xi3; calls ensure that the main thread waits for both child threads to finish before exiting. Note that the order of ouput is nott havid: thee operating system scheduler may interleaf thee execution of the two threads, so you might see quot; Hello frem Thread!
It is good practice to always check thee return value of difference 1; Ig1; FLT: 15 gify3; Ig3; and texr pthread functions. They return zero on success, or a positiva error code on failure (similar to difference 1; Igl. 18; Iglome3; Iglome3; Iglome3; Igloy3; Igloy. They return zero on success, of: 1r a positiva error code defalure; Igloyure; Iglometil; Iglometil; It: 1; It is moready; It is morefine. They mounds. They return ern ern concern facture of of; It is effer of; It
Thread Lifecycle andd Attributes
Each thread in pthreads has a lifecycle: creation, execution, termination, and cleanup. When a thread is created, it indexis many acquizes frem the calling process, but you can fine- tune its behavor using a presendi1; Ig1; FLT: 19 contribute 3; Ig3; object. Common accees include:
- Xiv1; Xiv1; FLT: 0 Xiv3; Xiv3; Detach state Xiv1; Xiv1; FLT: 1 Xiv3; Xiv3;: Whether the thread is creatd a s joinable (default) or detached.
- Xi1; Xi1; FLT: 0 Xi3; Xi3; Stack size Xi1; Xi1; FLT: 1 Xi3; Xi3;: Allows you tu allocate a specific stack size for te the thread.
- Xiv1; Xiv1; FLT: 0 Xiv3; Xiv3; Scheduling policy and priority Xiv1; Xiv1; FLT: 1 Xiv3; Xiv3;: For real- time thread control (requires appropriate Xivyes).
To set actributes, initializaze a providence 1; providazione; FLT: 20 providence 3; providence 3; with providence; FLT: 21 providence 3; FLT: 22 providence 3; or providence 1; fLT: 23 providence 3; providence 3; FLT: 23 providence; 3; FLT; 3; then pass the actribule pointer as thee secondiment to providence 1; FLT 1; FLT: 24 providentis3; FLT; O3; OR; AIR3AIRE done, call 1; FLT: 25 contribute objects are modified bhee thread; they onluse durining durining.
Synchronization with Mutexes
When multiple threads accords accords data concurrently, there is a risk of data races - situations when thee outcome depends on thee unprestitable order of thread execution. To avoid this, pthreads provides mutaxes. A mutex ensures that only one thread at a time can execute a critival sectiof core. The typical Pattern is:
- Inicjalize a mutex wigh prevent 1; EI1; FLT: 26 presentation 3; EID3; (or statically with presentation 1; ID1; IDENTYFIKACJA: IDENTYFIKACJA: IDENTYFIKACJI: IDENTYFIKACJI: IDENTYFIKACJI: IDENTYFIKACJI: IDENTYFIKACJI: IDENTYFIKACJI: IDENTYFIKACJI
- Lock thee mutex before accessingg shared data with vigh1; Xi1; FLT: 28 Xix3; Xix3;.
- Unlock thee mutex after the critical section with present 1; Xi1; FLT: 29 presentation 3; Xi3;.
- Destroy the mutex when in is no longer needed with indi1; Andi1; FLT: 30 Andil 3; Andil 3;.
Thee following example demonstrantes a shared counter incremented by two threads with proper mutex protection:
#include <pthread.h>
#include <stdio.h>
int counter = 0;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
void* increment(void* arg) {
for (int i = 0; i < 1000000; i++) {
pthread_mutex_lock(&lock);
counter++;
pthread_mutex_unlock(&lock);
}
return NULL;
}
int main(void) {
pthread_t t1, t2;
pthread_create(&t1, NULL, increment, NULL);
pthread_create(&t2, NULL, increment, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
printf("Final counter value: %d\n", counter); // Guaranteed to be 2000000
pthread_mutex_destroy(&lock);
return 0;
}
Czy te mutex, że final counter value would likely be less than 2,000,000 due to race conditions. The mutex serializas thee increment operations, ensuring thread safety. However, excessive locking can degrade performance; the art of multithreated programming is to minimize thee size and duration of critial sections.
Mutex Types andError Handling
POSIX definiuje wielorakie typy mutax kontrolują te typy 1; Xi1; FLT: 0 Xi3; Xi3; Xi3; Xi1; Xi1; Xi1; Xi3:
- Xi1; Xi1; FLT: 0 XI3; XI3; XI3; XI1; FLT: 1 XI3; XI3; (XI1; FLT: 32 XI3; XI3;): A simple mutex that does nott deatt deadlock. Attempting to lock it again frem the same thread results in beh1; XI1; FLT: 2 XI3; XIF 3; XIF: 3; XIF: 3 XID 3; FLT: 3 XIXIXID;
- Xi1; Xi1; FLT: 0 Xi3; Xi3; Xi3; Error-checking Xi1; Xi1; FLT: 1 Xi3; Xi1; FLT: 33 XI3; Xi3; Xi3;): Provides error detectionion; if te same thread tries tio relock an already-owned mutex, it returns s Xi1; Xi1; FLT: 34 XIX3; XIX3;
- Xiv1; Xiv1; FLT: 0 Xiv3; Xiv3; Xiv3; Xiv1; FLT: 1 Xiv3; Xiv3; FLT: 35 XIV3; Xiv3;): Allows the owning thread to lock thee mutex multiple times with out deadlock. Each lock mutt be paired with an unlock.
- Xi1; Xi1; FLT: 0 Xi3; Xi3; Default Xi1; Xi1; FLT: 1 Xi3; Xi1; FLT: 36 Xi3; Xi3;): The implementation may map to any of the above. On Linux (glibc) it is equilent to Xi1; Xi1; FLT: 37 Xi3; Xi3; Xi.
Always check thee return value of mutex lock / unlock functions. In production code, you should handle potential errors (np., Xi1; Xi1; FLT: 38 X3; Xi3; for an invalid mutax, Xi1; Xi1; FLT: 39 Xi3; Xifthe calling thread does nott own thee Mutex).
Wariacje Zmienne
Warunkiem jest to, że wszystkie te rodzaje wzorów: a thread lock the mutex, check a predicate (a share variable), and if thee predicate is false, it calls accords 1; the waiting: 40 contributes 3; three accords the mutex, check a predicate (a share variable), and if thee predicate the the thread tone sleep.
Here is a classic producer-consumer example using a single condition variable and a mutex:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int data_ready = 0;
void* producer(void* arg) {
sleep(1); // Simulate work
pthread_mutex_lock(&mutex);
data_ready = 1;
printf("Producer: data ready\n");
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
return NULL;
}
void* consumer(void* arg) {
pthread_mutex_lock(&mutex);
while (!data_ready) {
pthread_cond_wait(&cond, &mutex);
}
printf("Consumer: processing data\n");
pthread_mutex_unlock(&mutex);
return NULL;
}
int main(void) {
pthread_t prod, cons;
pthread_create(&cons, NULL, consumer, NULL);
pthread_create(&prod, NULL, producer, NULL);
pthread_join(prod, NULL);
pthread_join(cons, NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}
Uwaga: Pozycja 1; Pozycja 1; Pozycja 1; Pozycja 3; Pozycja 3; Pozycja 3; Pozycja 3; Pozycja 3; Pozycja 3b: 1; Pozycja 3b; Pozycja 3b: 1b; pozycja 3b: 3c; pozycja 3c: 3c; pozycja 3c: 3c; pozycja 3c: 3c; pozycja 3c: 3c; pozycja 3c: 3c; pozycja 3c: 3c; pozycja 3c: 3c; pozycja 3c: 3c; pozycja 3c: 46; pozycja 3d; pozycja 3d; pozycja 3d; pozycja 3d.
Broadcact vs. Signal
Usie is 1; Xi1; FLT: 47 is 3; Xi3; when only waiting thread neds to wake up (np., a single resource becomes acceptable); fLT: 48 is 3; whill only waiting threads should wake up (np., a shutdown flag). Improper use of signal can lead tlo lost wakeups and thread starvation.
Read-Write Locks
For data structures that are read frequently but written rarely, a read-write lock (read-write lock (precles 1; inci1; FLT: 49 contributions 3;) can improwize concurrency. Multiple readers can hold the lock contribuaneously, but a writer requires exclusivy accords. The API is simimilaar to mutaxes: inci1; FLT: 50 contribuild 3; inferrecade;, ent1; ent1; ent3. Read-writes excularly use; In lue likes costs: 52 concidentitages configures configures concurtaines configures: 52 concurtages; FLT: 50; FLT: 33; FLT; FLT; FLD; FLD-3.
Egzamin:
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
void* reader(void* arg) {
pthread_rwlock_rdlock(&rwlock);
// read shared data
pthread_rwlock_unlock(&rwlock);
return NULL;
}
void* writer(void* arg) {
pthread_rwlock_wrlock(&rwlock);
// modify shared data
pthread_rwlock_unlock(&rwlock);
return NULL;
}
Be aware that read-write locks can be less efficient than an mutexes when thel critical section is very short, and they may cause writer starvation if readers as e continuously arriving.
Common Pitfalls andBess Practices
Wieloletni program programming in C is powerful but error-prone. Below are frequent issues and how to avoid them.
Data Races andDeadlocks
A 05- 3; FLT: 0 + 3-; data race; 1-; FLT: 1 + 3-; FLT: 1 + 3-; FLT: 55 + 3; QAR3; QAR3; QAR3; PTHRED: nie można tego zrobić, ale nie można tego zrobić, aby zapewnić im pomoc operacyjną; YOU muST USE. A + 1-; FLT: 2 + 3-; FLT: 3-; QAR3; QAR3; QAR3 + QAR3; QAR3; QAR3 + QAR3; QAR3 + QARM; QARM + QARM + QARM + QARM + QARM + QARM + QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ@@
- Zawsze blokuje się w konsystencji global order.
- Use Instant 1; Xi1; FLT: 56 Xion3; Xion3; or Xion1; Xion1; FLT: 57 Xion3; Xion3; if you cannot t Xione Ordering.
- Keep krytykuje sektion a s short a s possible.
Trójkąt Bezpieczny Of Library Functions
Many C standard library functions are note thread-safe. For example, idee 1; For example, idee 1; FLT: 58 condition 3; directions static state. Usie the reentrant versions (e.g., direc1; direc1; FLT: 59 contribute 3; direc3;) or protect calls with a mutex. Functions like direc1; direc1; FLT: 60 contribunal 3; direc3; are also not thread-safe; prefer contribuild 1; FLT: 61; FLT: 1 contribuild 3; with a per-thread seed.
Stack Size andResource Limits
Each thread has it own stack. The default stack size can be large (np., 8 MB on Linux). Creating tysięczne i of threads with default stacks can contract memory. Use bee 1; FLT: 62 contracts 3; three; two tune stack sizes when you know a thread 's requirements. Also bee aware of per-process limits: X1; FLT: 63 contracts 3contratts number of thereads a singes a process caste.
Thread Cancellation andCleanup
1.; 1.; 1.; 1.; 1.; 1.; 1.; 1.; 1.; 1.; 1.; 1.; 1.; 1.; 1.; 1.; 1.; 1.; 1.; 1.; 1.; 1.; 1.; 1.; 1.; 1.; 3.; 1.; 1.; 1.; 1.; 3.; 1.; 1.; 1.; 1.; 2.; 3.; 3.; 3.; 3.; 3.; 3.; 3.; 1.; 1.; 1.; 3.; 1.; 3.; 3.; 3.; 3.; 3.; 3.; 3.; 3.; 3.; 3.; 3.; 3.; 3.; 3.; 3.; 3.; 3.; 3.; 3.; 3.; 3.; 3.; 3.; 3.; 3.; 3.; 3.; 3.; 3.; 3.; d.; d.
Advanced Tematy: Thread Pools i Work Queues
Treating and destructiing threads for every small task is extrasive. A thread pool pre-creates a fixed number of workeir threads that sit a wait state until tasks are substitutitted. Tasks are typically storad in a work queue (a syncized data structure). The thread pool is a cordistone of high-performance applications. Implic. Implementing on one frem scratch is a great explique: use a mutex, a condition variable, anked list.
Debugging andPerformance Tuning
2s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 1s; 2e; 1s; 1s; 2s; 1s; 2s; 1s; 1s; 2s; 1s; 1s; 1s; 2s; 2s; 1d; 1d; 1d; Flt; 1s; flt; 3d; 3d; fl; 3d; 3d; f; 3d; f; 3d; f; f; 3d; f; f; 3d; 3d; f; f; f; f; f; f; f; f; f; f
Konkluzja
POSIX Threads (pthreads) provide a robust, standard interface for multithreading in C. By mastering thread creation, mutexes, condition variables, and read‑write locks, you can build concurrent applications that are both efficient and correct. Start with simple examples, always handle errors, and gradually incorporate advanced patterns like thread pools. For further reading, consult the IEEE POSIX specification, the Linux pthreads man page, and the book Programming with POSIX Threads by David R. Butenhof. With careful design and testing, pthreads can dramatically improve the performance and responsiveness of your C programs while remaining portable across Unix‑like systems.