08-10-12, 05:42 PM
كاتب الموضوع : Islam Ibrahim
التطبيقات التي تقوم بتنفيذ عمليات متعددة في نفس الوقت تحتاج بطبيعة الحال لأن تبقى طوال الوقت مستجيبة ومستعدة لمعالجة العمليات التي يقوم بها المستخدم, يتطلب هذا الأمر عادة استخدام تعدد المسارات Multithreading , ومن أجل هذا تم تضمين مجالي الأسماء System.Threading و System.ComponentModel بمجموعة من الأدوات المناسبة لإنشاء التطبيقات متعددة المسارات ذات الأداء القوي, ولكن من أجل استخدام تلك الأدوات لا بد من أن يكون لديك دراية وخبرة كافية لتصميم تطبيق يعتمد على تعدد المسارات, وأحيانًا استخدام أداة في غير مكانها يتسبب في نتائج جانبية غير محمودة, ومن أجل ذلك يكون المكوّن BackgroundWorker هو الحل الأمثل في أغلب الأحيان, وهناك أحيان أخرى عند الحاجة لتنفيذ المهام بشكل غير متزامن تحتاج لبناء فئتك الخاصة وفقًا لنمط يُعرف بـ Event-based Asynchronous Pattern أو نمط العمليات الغير متزامنة والتي تعتمد على إطلاق الأحداث Events.استخدام نمط العمليات الغير متزامنة بالاعتماد على الأحداث, يقدم لنا تلك المزايا التي تتمتع بها التطبيقات متعددة المسارات, في حين يخفي في طياته الكثير من التعقيدات التي يمكن أن تنشأ عند القيام ببناء التطبيقات المتعددة المسارات, وعندما تقوم ببناء فئتك الخاصة بالاعتماد على هذا النمط, سيصبح بإمكانك:
- تنفيذ العمليات التي تستهلك وقتاً أطول أكثر من غيرها (مثل العمليات على قواعد البيانات, أو عمليات التحميل Downloading) في الخلفية بدون مقاطعة واجهة الاستخدام.
- تنفيذ عمليات متعددة في نفس الوقت, واستقبال تنبيهات عند انتهاء كل عملية
- الانتظار من أجل الحصول على مورد معيّن دون تعليق التطبيق وجعله عديم الاستجابة.
- التخاطب مع العمليات الغير متزامنة أثناء تنفيذها بالاستعانة بالأحداث والمفوضات Events & Delegates.
أداة التحكم PictureBox تعتبر مثالاً جيدًا لفئة تطبّق نمط العمليات الغير المتزامنة بالاعتماد على الأحداث , فيمكنك مثلاً تحميل صورة من موقع ما بشكل متزامن synchronously باستخدام الإجراء Load ولكن إذا كان حجم الصورة كبيرًا أو الاتصال بالإنترنت ضعيفًا فإن ذلك سيتسبب في توقف التطبيق عن الاستجابة Hanging حتى ينتهي تنفيذ الإجراء Load. وإذا أردت أن يحافظ التطبيق على استجابته ستحتاج عندها لاستخدام الإجراء LoadAsync , وهنا سيصبح بإمكانك معالجة الحدث LoadCompleted للأداة PictureBox تماماً كما تفعل مع أي حدث آخر, عندما تستدعي الإجراء LoadAsync فإن التطبيق يستمر في العمل والاستجابة وسيتم تنفيذ العملية في الخلفية في مسار مستقل عن المسار الأساسي لواجهة الاستخدام UI Thread , وسيتم استدعاء معالج الحدث LoadCompleted عند انتهاء العملية عندها يمكنك اختبار الوسيط من النوع AsyncCompletedEventArgs والذي سُيمرر لمعالج الحدث LoadCompleted للتحقق من نجاح عملية التحميل أم لا.
يتطلب نمط العمليات الغير المتزامنة هذا أن يكون هناك إمكانية لتعطيل العملية التي يتم تنفيذها بشكل غير متزامن , والأداة PictureBox بالفعل تحقق هذا المتطلب, حيث أنها تحتوي على الإجراء CancelAsync , عند استدعاء الإجراء CancelAsync سيتم إرسال طلب لإيقاف عملية التحميل التي يتم تنفيذها حالياً , وعند إيقاف العملية سيتم عندها إطلاق الحدث LoadCompleted.
مميزات هذا النمط أو لنموذج
هذا النمط أخذ عدة أشكال استنادًا إلى درجة تعقيد السيناريو الذي تنفذه كل فئة, أبسط فئة قد تحتوي على إجراء وحيد MethodNameAsync وحدث موافق لها MethodNameCompleted , الفئات الأكثر تعقيدًا قد تحتوي على أكثر من إجراء غير متزامن كل إجراء يمتلك الحدث الموافق له , يمكن أيضا أن يكون له في المقابل إجراء MethodName يتم تنفيذه بشكل متزامن.يمكن لهذه الفئات أيضًا أن تدعم إلغاء تنفيذ العملية الحالية Cancellation , تقرير حالة التقدم Report Progressing والنتائج المتراكمة عن كل عملية غير متزامنة.
إجراء غير متزامن يمكن أيضًا أن يدعم استدعاءات متتالية ومتداخلة بحيث لا يتم تنفيذ الاستدعاء الثاني للإجراء حتى ينتهي الاستدعاء الأول , وبهذه الحال يستطيع كودك استدعاء الإجراء أكثر من مرة قبل أن ينتهي من العملية الحالية . ولتنفيذ هذه الميزة قد يحتاج تطبيقك إلى تتبع تنفيذ كل عملية حتى تنتهي.
أمثلة:
تعتبر الفئتان PictureBox و SoundPlayer أبسط تمثيل لهذا النمط, بينما تمثيل الفئتان BackgroundWorker و WebClient لهذا النمط يعتبر أكثر تعقيدًا من سابقتيهما , المثال التالي يعتبر تجريد لفئة وهمية تنفذ هذا النمط:
VB Code
كود :
Public Class AsyncExample
' Synchronous methods.
Public Function Method1(ByVal param As String) As Integer
Public Sub Method2(ByVal param As Double)
' Asynchronous methods.
Overloads Public Sub Method1Async(ByVal param As String)
Overloads Public Sub Method1Async(ByVal param As String, ByVal userState As Object)
Public Event Method1Completed As Method1CompletedEventHandler
Overloads Public Sub Method2Async(ByVal param As Double)
Overloads Public Sub Method2Async(ByVal param As Double, ByVal userState As Object)
Public Event Method2Completed As Method2CompletedEventHandler
Public Sub CancelAsync(ByVal userState As Object)
Public ReadOnly Property IsBusy () As Boolean
' Class implementation not shown.
End Class
كود :
public class AsyncExample
{
// Synchronous methods.
public int Method1(string param);
public void Method2(double param);
// Asynchronous methods.
public void Method1Async(string param);
public void Method1Async(string param, object userState);
public event Method1CompletedEventHandler Method1Completed;
public void Method2Async(double param);
public void Method2Async(double param, object userState);
public event Method2CompletedEventHandler Method2Completed;
public void CancelAsync(object userState);
public bool IsBusy { get; }
// Class implementation not shown.
}
إعادة تحميل الإجراءات الغير متزامنة Overloading
عادة هناك نسختان من كل إجراء غير متزامن نسخة يتم استدعاؤها مرة واحدة فقط, ونسخة أخرى يمكن استدعاؤها أكثر من مرة (استدعاء متعدد) تمتلك معامل زائد userState هذه النسخة كما ذكرنا سابقًا تجعل من الممكن استدعاء الإجراء أكثر من مرة دون انتظار انتهاء الإجراء من القيام بالعملية الحالية, هذه النسخة يتم استدعاؤها بالشكل التالي:
كود :
Method1Async(string param, object userState)
كود :
Method1Async(string param)
تتبع العمليات التي في الانتظار.
إذا كنت تستخدم إجراء غير متزامن يعتمد على الاستدعاء المتعدد فإنك تحتاج إلى تتبع كل استدعاء بحيث كل استدعاء يتميز عن غيره بقيمة فريدة TaskID كما ذكرنا سابقًا إما GUID أو HashCode عند كل استدعاء للإجراء عليك القيام بتوليد تلك القيمة وإضافتها إلى HashTable حيث كل استدعاء يقابله ال userState الخاص به, عند انتهاء الاستدعاء الحالي يتم حذف ال userState والاستدعاء الخاص به من ال HashTable.
يمكنك الإطلاع على المثال التالي من MSDN لتتضح لك الصورة أكثر.
http://msdn.microsoft.com/en-us/library/9hk12d4y.aspx
وقريبًا جدًا سأضع مثال آخر يطبق نفس ال Pattern.
بالتوفيق