Table of Contents
مقدمة إلى خيوط POSIX
وتعد البرمجة المتعددة طريقة للبرمجة تتيح عملية واحدة تنفيذ مهام متعددة في وقت واحد، وبالتالي الاستخدام الأفضل لموارد وحدة البرامج القطرية وتحسين الاستجابة للطلبات، وفي لغة البرمجة C، فإن المعيار الأكثر اعتماداً فيما يتعلق بتعددية البرمجيات هو مكتبة الخيوط POSIX، التي يشار إليها عادة باسم [(FLT:0])] الخيوط [ترتيب المتزامن: 1).
وباستخدام الخيوط، يمكن للمطورين تصميم برامج تؤدي عمليات حساب خلفية، وتعالج علاقات متعددة مع العملاء في نفس الوقت، أو توازي مهام تجهيز البيانات، وتستخلص المكتبة تفاصيل عن نظام التشغيل المنخفض مع إعطاء ضبط دقيق لسلوك الخيوط، ويُعتبر الفهم القوي للخيوط أمرا أساسيا لأي مبرمج يعمل على تطبيقات مراعية للأداء أو متزامنة، وتتناول هذه المادة المفاهيم الأساسية، وتُقدم أفضل الممارسات.
المفاهيم الأساسية للخيوط
وقبل كتابة المدونة المتعددة التخصصات، من المهم أن يُطلع على أنواع البيانات الأساسية والوظائف التي تنص عليها الخيوط:
- Thread]: وحدة خفيفة الوزن من التنفيذ تجري في حيز عنوان العملية، وتتقاسم الخيوط المتعددة نفس الذاكرة، ومجندي الملفات، والموارد الأخرى، مما يجعل الاتصال بين الخيوط يتسم بالكفاءة، ولكن أيضاً يُدخل الحاجة إلى التزامن الدقيق.
- pthread t]: نوع من البيانات الشاذة المستخدمة لتمثيل محدد هوية الخيط، وليس ثلاجة؛ وينبغي أن تعامله على أنه مقبض يعاد بواسطة ويستخدم في المكالمات مثل .
- pthread create]: The function used to spawn a new thread. It takes four arguments: a pointer to a ] variable, a pointer to a thread attribute object (usually for defaults), the function the thread will execute (a
- نداء مُغلق ينتظر أن يُنهي خيط محدد، ويسترد أيضاً قيمة العودة من خيط البداية الروتيني
- pthread mutex t]: نوع البيانات لقفل متحول (الاستبعاد غير المتعمد)، وهو القفل الأساسي الذي يستخدم لحماية البيانات المشتركة من الوصول المتزامن.
- pthread cond t]: A condition alter used in conjunction with mutexes to allow threads to wait for specific conditions to become true.
(ب) أن تكون الخيوط مصممة في قابلة للكشف أو ] [، وبغير قصد، يمكن الجمع بين الخيوط، بمعنى أن هناك دعوة مماثلة تنظف مواردها، إذا لم تخطط للانضمام إلى شاشتها، يمكنك القيام بذلك.
تنفيذ برنامج بسيط متعدد التخصصات
The traditional demonstration of pthreads involves creating two threads that execute concurrently and printed messages. The example below illustrates the core steps: declaring ] variables, calling with a start routine, and then waiting for both threads to end with .
#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;
}
في هذا البرنامج، يخلق خيوطين، كل واحد منهما يدعو []] [يضمن] أن الخط الرئيسي ينتظر أن تنتهي أحذية الطفل قبل الخروج، ملاحظة أن ترتيب الناتج غير مضمون:
ومن الممارسات الجيدة التحقق دائماً من قيمة العودة لـ وغيرها من مهام الخياطة، فهي لا تحقق النجاح، أو رمز خطأ إيجابي بشأن الفشل (يذكر إلى ]) والبرنامج المذكور أعلاه يستخدم و لمعالجة الإخفاقات بشكل معقول.
دورة الحياة والعناصر
كل خيط في الخيوط له دورة حياة: الخلق، التنفيذ، الإنهاء، التنظيف، عندما يتم وضع خيط، يرث العديد من السمات من عملية الاتصال، ولكن يمكنك أن تضبط سلوكه باستخدام جسم .
- Detach state]: whether the thread is created as joinable (default) or detached.
- Stack size : يسمح لك بتخصيص حجم كيس محدد للخط.
- Scheduling policy and priority]: For real-time thread control (requires appropriate privileges).
To set attributes, initialize a with , modify it with functions like or , then pass the attribute pointer as the second argue to . When done, call . Attributh objects are not used.
التواؤم مع الموسيكس
وعندما يقترن العديد من الخيوط بالوصول إلى البيانات المشتركة في نفس الوقت، هناك خطر حدوث سباقات البيانات - الحالات التي تتوقف فيها النتيجة على الترتيب غير المتوقع لتنفيذ الخيوط، وتجنبا لذلك، توفر الخيوط المثبّتة المثومات، ويكفل المتحولون أن يكون الخيط الوحيد في وقت واحد هو الذي يمكن أن ينفذ جزءا حرجا من المدونة، والنمط المعتاد هو:
- Initialize a mutex with (or statically with ).
- اغلق المتحول قبل الوصول إلى بيانات مشتركة مع .]
- (د) فتح المتحول بعد القسم الحاسم مع .]
- Destroy the mutex when it is no longer needed with .
ويظهر المثال التالي وجود مضاد مشترك يتصاعد من خلال خيطين مع حماية متحولة على النحو المناسب:
#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;
}
وبدون المتحول، من المرجح أن تكون القيمة النهائية المقابلة أقل من 000 2 دولار بسبب ظروف العرق، ويُسجّل هذا المتحول عمليات التكديس، ويكفل السلامة من خيط التلف، غير أن الإقفال المفرط يمكن أن يتدهور الأداء؛ أما في البرمجة المتعددة المستويات فيتعين أن تقلل إلى أدنى حد من حجم الأقسام الحساسة ومدتها.
الموسم والتعامل مع الرعب
POSIX defines multiple mutex types controlled by the type] attribute:
- Normal] (): متحول بسيط لا يكشف عن طريق مسدود، يحاول قفله مرة أخرى من نفس الخيط الذي ينتج عنه ]deadlock .]
- Error‐ checking] (): Provides error detection; if the same thread tries to relock an already —owned mutex, it returns .
- Recursive] (): يسمح بخيط التملك لقفل المتحول مرات متعددة دون مأزق، ويجب أن يقترن كل قفل بفتح.
- Default] (): يمكن أن يرسم التنفيذ خريطة لأي من ما سبق.
(د) التحقق دائماً من قيمة العودة لوظيفة القفل/الغير المختلط، وفي رمز الإنتاج، ينبغي أن تتعامل مع الأخطاء المحتملة (مثلاً، بالنسبة لطيف غير صحيح، إذا لم يكن خيط الاتصال ملكاً للطين.
العوامل المتغيرة
وتسمح متغيرات الطوابق بأن تكون الخيوط في انتظار شرط محدد، وهي تستخدم دائماً مع المتحول، والنمط المثالي هو: قفل الخيوط، وتتحقق من الركيزة (متغير مشترك)، وإذا كانت الركيزة مزيفة، فإنها تدعو [(FLT:40]) التي تطلق الصدر الذروي وتعيد قراءة المادة (41)().
هنا مثال كلاسيكي للمنتجين المستهلكين باستخدام متغير حالة واحد و متحول
#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;
}
Note the cycle: a condition variable can suffer from spurious wakeups (a read may return from without actually being signaled). always re- check the predicate after waking up, not .
بث وقائع ضد موقع
(ب) استخدام عندما يحتاج فقط خبز واحد للانتظار (مثلاً، يصبح مورد واحد متاحاً)؛ واستخدام عندما ينبغي لجميع خيوط الانتظار أن تستيقظ (مثلاً، علم مقفل)، ويمكن أن يؤدي الاستخدام غير السليم للإشارة إلى فقدان الايقاظ والتجويع.
اقرأوا اقفال الشهود
وبالنسبة لهياكل البيانات التي يقرأها مراراً ولكن نادراً ما يكون القفل المكتوب بالكتابة () يمكن أن يحسن التطابق، ويمكن للقراء المتعددين أن يحتفظوا بالقفل في وقت واحد، ولكن الكاتب يحتاج إلى الوصول الحصري، ويشبه نظام التسجيل الإلكتروني المغنطيسي: ، ، [الكتاب المستوفى: 52].
مثال:
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;
}
كوني على علم بأن الأقفال المُطلية بالقراءة قد تكون أقل كفاءة من المُتَكَس عندما يكون القسم الحرج قصيراً جداً، وقد تسبب تجويع الكتاب إذا ما وصل القراء باستمرار.
الرواسب المشتركة وأفضل الممارسات
إن البرمجة المتعددة المرتجلة في جيم قوية ولكنها معرضة للخطأ، كما أن ما يليها هو قضايا متكررة وكيفية تجنبها.
مسارات البيانات والطرق
(أ) data race] occurs when two threads access the same memory location without coincidehronization and at least one write. C11 added the ]] qualifier, but pthreads does not providetom operations directly; you must use locks. A]deadlock[FLT:
- دائماً ما تكتسب أقفال في نظام عالمي ثابت
- استخدمي أو إذا لم تستطع ضمان الطلب.
- ابقوا الأقسام الحرجة في اقصر وقت ممكن
سلامة وظائف المكتبة
وكثير من وظائف المكتبة المعيارية في الفئة جيم ليست آمنة، فعلى سبيل المثال، تستخدم ] الدولة الثابتة الداخلية، وتستخدم النسخ المعاد إدخالها (مثلاً، ]]) أو تحمي المكالمات مع المتحولين، كما أن المهام مثل ليست مأمونة من حيث القراء؛ ويفضل مع بذور موحدة]
حجم الحزمة والحدود من الموارد
ويمكن أن يكون حجم الكيس غير المقصود كبيراً (مثلاً 8 ميغابايت على لينوكس)، ويمكن أن يستنفد الذاكرة إنشاء آلاف الخيوط التي تحتوي على أكوام غير مقصودة، واستخدام لتقلب أحجام الكسر عند معرفة متطلبات الخيوط، كما يمكن أن يكون على علم بالحدود القصوى
إلغاء الخيط والتنظيف
يمكن إلغاء خيط ويمكن أن يكون الإلغاء متزامناً (الاستعراض الفوري) أو [الاستعمال المخفف] [العمل على إزالة القفل] (حتى تصل الخيوط إلى نقطة إلغاء مثل أو [الإلغاء الفوري: 66]
المواضيع المتقدمة: مجموعات الأحذية ومجلات العمل
إن إنشاء وتدمير الخيوط لكل مهمة صغيرة مكلفة، ويفترض مجموعة الخيوط وجود عدد ثابت من خبوط العمال التي تتواجد في ولاية انتظار حتى تقدم المهام، وتخزن المهام عادة في إطار عمل متزامن (هيكل متغير للبيانات) وقاعدة القراء هي حجر الزاوية في تطبيقات الخواديم العالية الأداء، ويُعد تنفيذ نظام من الخدوش عملية كبيرة: استخدام إطار متغير للبيانات.
دال - الديون وتقييم الأداء
For Multithreaded fines are notoriously difficult to reproduce and fix. Use tools like Valgrind’s Helgrind or ThreadSanitizer] (enable by compiling with [FuxT:69])
خاتمة
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.