Table of Contents
הקדמה ל-POSIX
Multithread היא טכניקת תכנות המאפשרת תהליך אחד לבצע משימות מרובות במקביל, ובכך לעשות שימוש טוב יותר משאבי CPU ושיפור תגובת היישום. בשפת התכנות C, תקן מאומצת ביותר עבור ריבוי מערכות כמו יוניקס הוא ספריית POSIX POSIX נספחים, המכונה בדרך כלל כמו FLT:0pthreads לינוקס לינוקס 1LT:1 ה- API מספק מערכת מקיפה של פונקציות POSIX ו-Riceration הוא להגדיר כמעט סטנדרטית של ניהול סטנדרטית.
באמצעות pthreads, מפתחים יכולים לתכנן תוכניות לביצוע חישובים רקע, להתמודד עם קשרים מרובים של לקוחות במקביל, או במקביל למשימות עיבוד נתונים.הספריה מופשטת מפרטי מערכת הפעלה ברמה נמוכה תוך מתן שליטה על התנהגות חוטית. הבנה מוצקה של pthreads הוא חיוני עבור כל מתכנתים C עובד על יישומים רגישים ביצועים או במקביל. מאמר זה מכסה את המושגים היסוד, מספק קוד מעשי, וחוקרים שיטות פרימיטיביות.
מושגים בסיסיים של pthreads
לפני כתיבת קוד רב-הנקרא, חשוב להכיר את סוגי הנתונים הבסיסיים ואת הפונקציות שפצצות מספק:
- (ב) [ה]: יחידה קלה של ביצוע הפועלת בתוך מרחב הכתובת של תהליך.מספר חוטים חולקים את אותו זיכרון, תיאורי קבצים ומשאבים אחרים, מה שהופך את התקשורת בין חוטים יעילים אך גם מציג את הצורך בסנכרון זהה.
- (ב) ויקרא:א): "בְּאֶתָּעָה אֲשֶׁר נָאוּ לְךָ עַמֶר עַמֶרְטַהָּעָה אֲשֶׁר אִם עַמְתָּעָה" (בראשית כ"ד, כ"ד)
- (ב) [ה]: [ה], [ה], [ה],] [ה], [ה], [ה]], [ה],] [ה], [ה]ה'], [ה']'[ה]']'[ה']'[ה']'[ה']']'''''[ה']']'[ה'[ה']']'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
- (ב) ויקרא:א): "הקריאה המחסמה שמחכה לחוט מסוים לסיים.הוא גם מחזיר את ערך החזרה מהשגרה הראשונה של החוט.
- (FLT:0)pthread mutex tcioFLT:1; סוג הנתונים עבור מנעול mutex (הדרה מילולית) שהוא פרימיטיבי הסינכרון הבסיסי המשמש כדי להגן על נתונים משותפים מגישה זו.
- (ב) [15] ויקרא: ויקרא: ויקרא י"ד): "מבחינתו של שינוי מצב המשמש בשילוב עם מוטטורים כדי לאפשר לחוטים להמתין לתנאים מסוימים כדי להפוך לאמת.
(ב) , ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇
יישום תוכנית פשוטה Multithreaded
ההפגנה הקלאסית של הטארכות כוללת יצירת שני חוטים המבצעים הודעות במקביל ודפיסים.הדוגמה להלן ממחישה את השלבים הבסיסיים: להכריז על משתנים של FLT:8, קורא ל-FLT:9 עם שגרת התחלה, ולאחר מכן לחכות ששני החוטים יסתיימו עם FLT:10.
#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;
}
בתכנית זו, בורא שני חוטים, שכל אחד מהם קורא (FLT:13 ; ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇
(הופנה מהדף "הטוב" (ב) הוא תמיד לבדוק את ערך ההחזר של ה-FLT:15 ופונקציות אחרות של קריאה (הם חוזרים אפס על הצלחה, או קוד שגיאה חיובי על כישלונות (כמו למשל ל-FLT:16).
תגיות: Lifecycle and Attributes
לכל חוט בפצחים יש מחזור חיים: יצירה, הוצאה להורג, סיום וניקוי, כאשר נוצר חוט, הוא יורש תכונות רבות בתהליך הקריאה, אך ניתן לנקז את התנהגותו באמצעות אובייקט FLT:19.
- (ב) ,0) , ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇
- (ב) ,0) גודלו של ה- 1 (ב) מאפשר לך להקצות גודל ערימה מסוים עבור החוט.
- (ב) ,0) מדיניות ועדיפות ראשונה: עבור שליטה בזמן אמת (requires המתאים זכויות).
(ב) ל[[1924]], [[1924]], [[1924]], [[1924]]]], [[1924]]]], [[1924]]]], [[1924]]]]]], [[1924]]]]]], [[1924]]]]]]]], [[1924]]]]]]]], [[1924]]]]]]]], [[1924]]]]]]]]
סינכרון עם Mutexes
כאשר חוטים מרובים גישה לנתונים משותפים במקביל, קיים סיכון של מרוצי נתונים - מצבים שבהם התוצאה תלויה בסדר בלתי צפוי של ביצוע חוט.כדי להימנע מכך, pthreads מספק mutexes. A mutex מבטיח שרק חוט אחד בזמן יכול לבצע סעיף קריטי של קוד.
- מדרש (ב) , ויקרא י"ד (ב"ד)
- הורידו את ה- mutex לפני שהצטרפו לנתונים משותפים עם FLT:28
- [ה] לא ליישב את ה-[[המאה ה-21]].
- להשמיד את ה- mutex כאשר כבר לא צריך עם FLT:30
הדוגמה הבאה מציגה ניגוד משותף המובא על ידי שני חוטים עם הגנה מוטטקסית נכונה:
#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;
}
ללא ה- mutex, ערך הדלפק הסופי יהיה פחות מ 2 000 בשל תנאי גזע.ה mutex מארגן את פעולות ההקפיות, הבטחת בטיחות חוט.עם זאת, מנעול יתר יכול לזלזל בביצועים; האמנות של תכנות רב-פעמי היא למזער את הגודל והמשך של חלקים קריטיים.
סוגי Mutex וטעויות Handling
POSIX מגדיר סוגים רבים של mutex נשלט על ידי ה-FLT:0type (ה) 1 תכונת:
- (ב) [ה]: [ה], [ה], [ה]], [ה], [ה], [ה]], [ה], [ה], [ה]]]], [ה'], [ה'], [ה'], [ה']'[ה']'[ה']']'[ה']']']'[ה'[ה']']']'[ה']']'[ה'[ה']'[ה'[ה'[ה'[ה'[ה'[ה']']']']'[ה']']'[ה']']']']']']']'[ה'[ה'[ה']']']']']']'[ה'[ה']']'[ה'[ה'[ה']']']'[ה']']']'[ה'[ה']'[ה']'[ה'[ה']']'[ה'[ה'[ה'[ה'
- (ב) ויקרא י"א): "וַיְהַּהְיִדָּבְתָּבְתָּבְתָּבָה" (במדבר כ"ד, כ"ד)
- (ב) ויקרא י"ד): "הנחה" (ה') "הנחה" (ה') "הנחה" (ה') היא זו ש"ה') "הנחה" (ה') "הנחה את ה'"ה' (ה')" (ה')"ה')" (ה')"ה')"ב"ה')
- (ב) ,0) ,(ה) ,(ה) , ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇
תמיד לבדוק את הערך של פונקציות מנעול / ביטול של מוצר, עליך להתמודד עם שגיאות פוטנציאליות (למשל, FLT:38 עבור mutex לא יסולא בפז, FLT:39 אם חוט הקריאה אינו הבעלים של mutex).
המונחים:
משתנים מאפשרים חוטים לחכות למצב מסוים כדי להיות אמיתי.הם תמיד בשימוש עם mutex.התבנית האופיינית היא: חוט מנעול את ה- mutex, בודקים את הניקוד (משתנה משותף), ואם ה-Predicate הוא שקר, הוא קורא ל-FLT:40 אשר משחרר את ה- mutex ומכניס את החוט לישון.
הנה דוגמה קלאסית למפיק-consumer באמצעות משתנה מצב אחד ו- 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;
}
(ב) ויקרא ט' (ב) , ויקרא י"א): "ה' (ב"א) ,ב"ה' (ב"ב) ,"ה' (ב"ב) ,"ה', "ה'לא יברך את ה'" (במדבר כ"ב).
שידור לעומת אותות
(ב) כאשר רק חוט המתנה אחד צריך להתעורר (למשל, משאב אחד הופך זמין); השתמש ב-FLT:48 כאשר כל חוטי ההמתנה צריכים להתעורר (למשל, דגל חסימה) שימוש בסימן יכול להוביל להתעוררות אבודות ורעב.
תגית: Write Locks
עבור מבני נתונים אשר קוראים לעתים קרובות אך נכתב לעתים רחוקות, מנעול קורא (FLT:49) יכול לשפר את הקונפלי.מספר קוראים יכולים להחזיק את המנעול בו זמנית, אך סופר דורש גישה בלעדית.ה API דומה ל- mutexes: (FLT:50, FLT:51, FLT:52, ו-FLT:53 מנעולים שימושיים במיוחד בתרחישים כגון תצורה או טבלאות מעוגלות.
דוגמה:
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;
}
להיות מודע לכך שמנעולים של כתיבה יכול להיות פחות יעיל מאשר ממורשמים כאשר החלק הקריטי הוא קצר מאוד, והם עלולים לגרום לרעב סופר אם הקוראים מגיעים ללא הרף.
מלכודות נפוצות ועיסוקים טובים
תכנות רב-הנקרא ב- C הוא חזק אך שגיאות-פרון להלן הן בעיות תכופות וכיצד להימנע מהן.
מרוץ נתונים ו Deadlocks
(ב) ב[[1924]], [[1924]], [[1924]]]], [[1924]]]], [[1924]]]], [[1924]]]], [[1924]]]]]]]], [[1924]]]]]]]], [[1924]]]]]]]]]]]]]], [[1924]]]]]]]]]], [[1924]]]]]]]]]]]]]], [[1924]]]]]]]]]]]]]], [[1924]]]]]], [[1924]]]]]]]]]], [[1924]]]], [[1924]]]]]]]]]]]]]]]]]]]], [[1924]]]]]]]]]], [[1924]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]], [[1924]]]], [[1924]]]]]]]], [[1924]], [[1924]], [[1924]], [[1924]], [[1924]]]], [[1924]], [[1924]], [[1924]], [[1924]]]]]]]], [[1924]]]]]]]], [[1924]]]]]]]], [[1924]], [[19
- תמיד לרכוש מנעולים בסדר עולמי עקבי.
- (ב) אם לא ניתן להבטיח את הסדר.
- שמור על קטעים קריטיים כמה שיותר קצרים.
המונחים: Library function
פונקציות ספריות סטנדרטיות רבות אינן בטוחות חוט, למשל, משתמשות במדינה סטטית פנימית. השתמש בגרסאות הרותנית (למשל, FLT:59) או להגן על שיחות עם מרטוט.
גודל והגבלות משאבים
לכל חוט יש ערימה משלו.גודל ערימה ברירת המחדל יכול להיות גדול (למשל, 8 MB על לינוקס) יצירת אלפי חוטים עם מחסניות ברירת מחדל יכול להשמצה זיכרון.שימוש ב-FLT:62 כדי להתאים את גודל הערימה כאשר אתה מכיר את דרישות חוט.
ביטול וניקוי
(ב) ניתן לבטל את ה[[המאה ה-20]], [[1924]], [[1924]], [[1924]]]], [[1924]]]], [[1924]]]]]], [[1924]]]]]], [[1924]]]]]]]]]], [[1924]]]]]]]]]], [[1924]]]]]]]]]], [[1924]]]]]]]]]], [[1924]]]]]]]]]]]]]], [[1924]]]]]], [[1924]]]], [[1924]]]]]], [[1924]]]]]]]]]]]]]]]]]], [[1924]]]]]]]]]]]]]], [[1924]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
נושאים מתקדמים: מחרוזת ועבודות
יצירת וחיסול חוטים עבור כל משימה קטנה יקר. בריכת חוט לפני יצירת מספר קבוע של חוטי עבודה שיושבים במצב המתנה עד משימות הוגשו.משימות מאוחסנות בדרך כלל בתור עבודה (מבנה נתונים סינכרוני) בריכת חוט זהה אבן הפינה של יישומים בשרתים בעלי ביצועים גבוהים. יישום אחד מאפס הוא תרגיל גדול: שימוש במישרין, מצב משתנה, ומסגרת של פונקציות חיצוניות (חלקיות) כמו קוד פתוח (חלק) אבל שימוש רבות של ספריות חיצוניות).
דיון וביצועים Tuning
(ב) ,ברבים (ברבים) יש קושי רב להתרבות ולתקנון כלים כמו:0Valgrindd's HelgrindFLT:1 או FLT:2ThreadSanizer (בקיצור של תפוצה) ,5 , 000 ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇
מסקנה
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.