البرمجة غير المتزامنة في C#: دليل شامل للمبتدئين
\n\nالبرمجة غير المتزامنة هي أحد المفاهيم الأساسية في التطوير الحديث باستخدام C#. فهي تتيح لتطبيقاتك البقاء مستجيبة دون حظر تيار التنفيذ أثناء العمليات الطويلة: قراءة الملفات، استعلامات قاعدة البيانات، أو استدعاءات HTTP. في هذا الدليل، سنشرح أساسيات التزامن غير المتزامن في C# مع أمثلة عملية.
\n\nما هي البرمجة غير المتزامنة ولماذا نحتاجها؟
\n\nفي البرمجة المتزامنة، يتم تنفيذ الكود بشكل تسلسلي. إذا قمت باستدعاء طريقة تنتظر ردًا من الخادم لمدة 5 ثوانٍ، فإن التيار بأكمله (واجهة المستخدم أو تيار العمل) يتجمد. يتوقف التطبيق عن الاستجابة. تحل البرمجة غير المتزامنة هذه المشكلة: بدلاً من حظر التيار، تعيد الطريقة التحكم إلى الكود المستدعي، وعندما تكون النتيجة جاهزة، تقوم بإخطاره بذلك.
\n\nالمزايا الرئيسية للتزامن غير المتزامن:
\n- \n
- استجابة واجهة المستخدم — لا تتجمد الواجهة أثناء العمليات الطويلة. \n
- قابلية التوسع — يمكن لتطبيقات الخادم معالجة المزيد من الطلبات دون إنشاء تيارات جديدة. \n
- الكفاءة — تُستخدم التيارات المحررة لمهام أخرى. \n
أساسيات async/await في C#
\n\nالكلمتان المفتاحيتان async و await هما أساس البرمجة غير المتزامنة في C#. ظهرتا في C# 5.0 وجعلتا الكود غير المتزامن بسيطًا تقريبًا مثل الكود المتزامن.
قواعد الاستخدام:
\n- \n
- يتم تمييز الطريقة بالكلمة المفتاحية
async. \n - يجب أن يكون نوع الإرجاع
TaskأوTask<T>أوvoid(فقط لمعالجات الأحداث). \n - داخل الطريقة، استدعِ طرقًا غير متزامنة أخرى باستخدام الكلمة المفتاحية
await. \n
مثال بسيط:
\nusing System;\nusing System.Net.Http;\nusing System.Threading.Tasks;\n\nclass Program\n{\n static async Task Main(string[] args)\n {\n Console.WriteLine("نبدأ التحميل...");\n string content = await DownloadWebPageAsync("https://example.com");\n Console.WriteLine($"تم تحميل {content.Length} حرفًا");\n }\n\n static async Task<string> DownloadWebPageAsync(string url)\n {\n using HttpClient client = new HttpClient();\n string result = await client.GetStringAsync(url);\n return result;\n }\n}\n\nلاحظ أن الطريقة Main مميزة أيضًا بـ async Task — وهذا مسموح به بدءًا من C# 7.1.
الأنواع Task و Task<T> و ValueTask
\n\nفي البرمجة غير المتزامنة في C#، تُستخدم ثلاثة أنواع رئيسية لقيم الإرجاع:
\n\n- \n
Task— للطرق التي لا تُرجع قيمة (مشابه لـ void). \n Task<T>— للطرق التي تُرجع قيمة من النوع T. \n ValueTask<T>— نسخة محسّنة للسيناريوهات التي تكون فيها النتيجة متاحة بشكل متزامن غالبًا (مثل من ذاكرة التخزين المؤقت). \n
مثال مع Task<T>:
\nstatic async Task<int> CalculateSumAsync(int a, int b)\n{\n await Task.Delay(1000); // محاكاة عمل طويل\n return a + b;\n}\n\n// الاستدعاء:\nint result = await CalculateSumAsync(5, 3);\nConsole.WriteLine(result); // 8\n\nمعالجة الأخطاء في الطرق غير المتزامنة
\n\nيتم معالجة الاستثناءات في الطرق غير المتزامنة باستخدام كتلة try-catch القياسية. ومع ذلك، هناك فارق مهم: يتم حفظ الاستثناء في كائن Task ويتم "إلقاؤه" مرة أخرى عند استخدام await.
static async Task<string> ReadFileAsync(string path)\n{\n try\n {\n string content = await File.ReadAllTextAsync(path);\n return content;\n }\n catch (FileNotFoundException ex)\n {\n Console.WriteLine($"الملف غير موجود: {ex.Message}");