البرمجة غير المتزامنة في C++: من std::async إلى coroutines
البرمجة غير المتزامنة في C++ هي أداة قوية لإنشاء تطبيقات عالية الأداء وسريعة الاستجابة. في العالم الحديث، حيث أصبحت المعالجات متعددة النوى معيارًا، فإن القدرة على إدارة المهام المتوازية بكفاءة هي مهارة أساسية لكل مطور C++. في هذه المقالة، سنقوم بتحليل المفاهيم الأساسية، بدءًا من الآليات الأساسية للمكتبة القياسية وصولاً إلى coroutines الحديثة من C++20.
لماذا نحتاج البرمجة غير المتزامنة؟
الهدف الرئيسي للبرمجة غير المتزامنة هو عدم حظر التدفق الرئيسي لتنفيذ البرنامج. تخيل أن تطبيقك يحتاج إلى تحميل ملف من الشبكة، ومعالجته، ثم عرضه للمستخدم. إذا قمت بتنفيذ هذه العمليات بشكل متزامن (synchronous)، ستتجمد الواجهة أثناء التحميل. النهج غير المتزامن يسمح بتشغيل العملية الطويلة "في الخلفية"، بينما يستمر التدفق الرئيسي في معالجة إدخال المستخدم.
النهج الرئيسية للبرمجة غير المتزامنة في C++
في C++، توجد عدة طرق لتحقيق التنفيذ غير المتزامن:
- الإدارة اليدوية للخيوط (std::thread) — نهج منخفض المستوى ولكنه مرن.
- std::async و std::future — آلية عالية المستوى لتشغيل المهام غير المتزامنة.
- std::packaged_task و std::promise — للتحكم الدقيق.
- Coroutines (C++20) — طريقة حديثة وأنيقة لكتابة الكود غير المتزامن.
استخدام std::async و std::future
هذه هي أبسط طريقة لتشغيل مهمة غير متزامنة. الدالة std::async تُرجع كائنًا من نوع std::future، والذي سيحتوي على نتيجة التنفيذ.
#include <iostream>#include <future>#include <thread>#include <chrono>
int heavy_computation(int x) { std::this_thread::sleep_for(std::chrono::seconds(2)); return x * x;}
int main() { std::cout << "نقوم بتشغيل مهمة غير متزامنة..."; std::future<int> result = std::async(std::launch::async, heavy_computation, 42); // نقوم بشيء آخر أثناء تنفيذ المهمة std::cout << "التدفق الرئيسي غير محظور!"; // الحصول على النتيجة (يتم الحظر إذا لم تكتمل المهمة بعد) int value = result.get(); std::cout << "النتيجة: " << value << ""; return 0;}العلم std::launch::async يضمن تنفيذ المهمة في خيط منفصل. إذا لم يتم تحديده، قد يؤجل التنفيذ حتى استدعاء .get().
العمل مع std::promise و std::packaged_task
توفر هذه الآليات تحكمًا أكبر. std::promise يسمح بـ "الوعد" بقيمة سيتم تعيينها لاحقًا، بينما std::packaged_task يغلف كائنًا قابلًا للاستدعاء في مهمة.
#include <iostream>#include <future>#include <thread>
void set_value(std::promise<int>&& prom) { std::this_thread::sleep_for(std::chrono::seconds(1)); prom.set_value(100);}
int main() { std::promise<int> prom; std::future<int> fut = prom.get_future(); std::thread t(set_value, std::move(prom)); std::cout << "في انتظار النتيجة..."; int value = fut.get(); std::cout << "تم الاستلام: " << value << ""; t.join(); return 0;}هام: std::promise و std::future هما كائنان قابلان للنقل فقط (move-only). لا يمكن نسخهما، بل نقلهما فقط.
Coroutines في C++20: مستوى جديد من البرمجة غير المتزامنة
Coroutines هي دوال يمكنها تعليق تنفيذها واستئنافه لاحقًا. تسمح بكتابة كود غير متزامن كما لو كان متزامنًا (synchronous).