التزامن وتعدد الخيوط في لغة C++: دليل شامل

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة في التزامن وتعدد الخيوط في C++



تتطلب التطبيقات الحديثة أقصى أداء. يسمح التزامن وتعدد الخيوط في C++ بالاستخدام الفعال للمعالجات متعددة النوى، مما يسرع تنفيذ المهام. بدون فهم هذه المفاهيم، من المستحيل إنشاء أنظمة عالية التحميل أو معالجات بيانات أو محركات ألعاب.



في هذه المقالة، سنقوم بتحليل الآليات الرئيسية لتعدد الخيوط في C++، بدءًا من std::thread الأساسي وصولاً إلى الأنماط المتقدمة. ستتعلم كيفية تجنب سباقات البيانات، ومزامنة الخيوط بشكل صحيح، وكتابة كود متوازي آمن.



أساسيات تعدد الخيوط: std::thread و std::jthread



الفئة std::thread (C++11) هي أساس البرمجة متعددة الخيوط. تسمح بتشغيل دالة أو كائن قابل للاستدعاء في خيط منفصل. لنلق نظرة على أبسط مثال:



#include <iostream>#include <thread>

void worker(int id) { std::cout << "الخيط " << id << " بدأ العمل"; std::this_thread::sleep_for(std::chrono::seconds(1)); std::cout << "الخيط " << id << " اكتمل";}

int main() { std::thread t1(worker, 1); std::thread t2(worker, 2); t1.join(); t2.join(); return 0;}


نقاط رئيسية:

  • join() — الخيط الرئيسي ينتظر اكتمال الخيط الفرعي
  • detach() — يستمر الخيط في العمل بشكل مستقل (خطر تسرب الموارد)
  • std::jthread (C++20) — يستدعي join() تلقائياً في المدمر


تحقق دائماً من joinable() قبل استدعاء join() لتجنب الانضمام المزدوج.



مزامنة الخيوط: الميوتكسات والأقفال



المشكلة الرئيسية في تعدد الخيوط هي سباقات البيانات، عندما تقوم عدة خيوط بتعديل متغير مشترك في نفس الوقت. للحماية، تُستخدم الميوتكسات.



std::mutex و std::lock_guard



#include <iostream>#include <thread>#include <mutex>

std::mutex mtx;int counter = 0;

void increment() { for (int i = 0; i < 100000; ++i) { std::lock_guard<std::mutex> lock(mtx); ++counter; }}

int main() { std::thread t1(increment); std::thread t2(increment); t1.join(); t2.join(); std::cout << "النتيجة: " << counter << ""; // 200000 return 0;}


std::lock_guard يحرر الميوتكس تلقائياً عند الخروج من النطاق. للتحكم الأكثر مرونة، استخدم std::unique_lock.



الجمود (Deadlock) و std::scoped_lock



يحدث الجمود عندما ينتظر خيطان بعضهما البعض. الحل هو استخدام std::scoped_lock (C++17)، الذي يلتقط عدة ميوتكسات بشكل ذري:



std::mutex m1, m2;

void safe_function() { std::scoped_lock lock(m1, m2); // عمل آمن مع موردين}


الخوارزميات المتوازية و std::async



لتنفيذ المهام غير المتزامنة دون إدارة يدوية للخيوط، استخدم std::async. يعيد std::future، الذي يمكن من خلاله الحصول على النتيجة.



#include <iostream>#include <future>#include <chrono>

int compute(int n) { std::this_thread::sleep_for(std::chrono::seconds(n)); return n * n;}

int main() { auto future1 = std::async(std::launch::async, compute, 2); auto future2 = std::async(std::launch::async, compute, 3); std::cout << "النتائج: " << future1.get() << ", " << future2.get() << ""; // 4, 9 return 0;}


سياسات التشغيل:

  • std::launch::async — تشغيل في خيط جديد
  • std::launch::deferred — تنفيذ كسول (عند استدعاء get())

المدونات

توصيات الكتب