دعم تعدد الخيوط في عملاء البريد
يسمح عملاء البريد مثل ImapClient و Pop3Client للمستخدمين باستخدامهم في بيئة متعددة الخيوط. يسمح عملاء البريد بوجود اتصال واحد أو أكثر مع الخادم. لإدارة مجموعة من الاتصالات داخل العملاء يتم استخدام مجموعة الاتصالات (connection pool). عدد الاتصالات التي يمكن إنشاؤها واستخدامها في نفس الوقت محدود بالخاصية CredentialsByHostClient.MaxConnectionsPerServer. يمكن ضبط هذه الخاصية إلى 1 أو قيمة أعلى. القيم الافتراضية لهذه الخاصية هي 10. تم تنفيذ طابور الأوامر للاتصال لدعم عمليات متعددة الخيوط. الأوامر تنفّذ أبسط العمليات المعرفة في البروتوكول، مثل Noop، Authenticate وهكذا. قد يبدأ المستخدم تنفيذ عدد أكبر من الأوامر مقارنة بعدد الاتصالات المتاحة. ولكن التنفيذ سيحدث فقط عندما يتمكن العميل من إنشاء اتصال للقيام بالعملية.
طرق استخدام عملاء البريد في بيئة متعددة الخيوط
عملاء البريد الإلكتروني يتبعون السلوك التالي:
1. في حالة كون MaxConnectionsPerServer = 1، ينشئ العميل اتصالًا واحدًا، ويجري المصادقة والتفويض. يظل هذا الاتصال في حالة تشغيل حتى يتم إيقاف العميل. تُوجه جميع العمليات من خيوط مختلفة إلى طابور أوامر واحد موضوع في الاتصال الرئيسي.
2. في حالة كون MaxConnectionsPerServer > 1، ينشئ العميل العدد اللازم من الاتصالات، ويجري المصادقة والتفويض لكل اتصال. يُخصَّص أحد الاتصالات كاتصال رئيسي. يظل هذا الاتصال في حالة تشغيل حتى يتم إيقاف العميل. تُنشأ وتُهدم باقي الاتصالات حسب الحاجة. يُحدَّد الحد الأقصى لعدد هذه الاتصالات بواسطة الخاصية MaxConnectionsPerServer، على سبيل المثال إذا كان MaxConnectionsPerServer = 2 فإن أحدها يُخصَّص كاتصال رئيسي، والاتصال الثاني يُستخدم كإضافي للعمليات التي تُنفّذ في خيوط أخرى. وبالمثل إذا كان MaxConnectionsPerServer = 3 فإن الاتصال الأول يُخصَّص كاتصال رئيسي، والاتصالين الآخرين يُستخدمان كإضافيين للعمليات في خيوط أخرى. إذا وصل طلب اتصال جديد من خيط جديد وجميع الاتصالات مشغولة، ينتظر العميل حتى ينخفض عدد الاتصالات المستخدمة. هذه نقطة مهمة توضح لماذا يعتبر التخلص الصحيح من الاتصالات أمرًا ضروريًا.
أمثلة
يمكن للمستخدم تنفيذ العمليات في خيوط مختلفة بطرق متعددة. يمكن تقسيمها إلى نوعين:
1. يستخدم المستخدم الطرق غير المتزامنة (Begin/End) المعرفة في العميل. في هذه الحالة يطلق عميل البريد خيوطًا جديدة عند الحاجة. يُنفّذ العميل طابور مهام (يرجى عدم الخلط بينه وبين طابور الأوامر في الاتصال). يمكن تنفيذ المهمة إذا كان الاتصال متاحًا. بمجرد أن يصبح عدد الاتصالات المستخدمة أقل من قيمة الحد، ينشئ العميل اتصالًا جديدًا، وينشئ خيطًا للمهمة الحالية وينفّذها. مثال على استخدام العمليات غير المتزامنة:
2. يمكن للمستخدم إنشاء خيوط باستخدام كائنات مثل Thread أو ThreadPool أو Task أو أي كائنات أخرى مخصصة لهذا الغرض. يمكن أيضًا استخدام الخيوط التي أنشأتها شيفرة طرف ثالث. خلال ذلك يمتلك العميل نموذجين سلوكيين
أ. إذا لم يقم المستخدم بإنشاء اتصالات إضافية للعمليات في الخيط، فسيتم إرسال جميع عمليات هذا الخيط إلى طابور الأوامر عبر الاتصال الرئيسي. مثال على عمليات في خيط إضافي بدون إنشاء اتصال جديد، جميع المعاملات تتم عبر الاتصال الرئيسي:
ب. عندما يقوم المستخدم بتشغيل طريقة لإنشاء اتصال جديد لخيط إضافي، يُحجب هذا الخيط حتى يتم تعديل قيمة الحصة الخاصة بالاتصالات الجديدة للسماح بالاتصال الجديد. ثم يُنشأ الاتصال الجديد. يُعيّن هذا الاتصال كاتصال افتراضي لجميع العمليات في هذا الخيط. بعد إتمام جميع العمليات في هذا الخيط يجب التخلص من الاتصال. لإنشاء اتصالات جديدة استخدم طريقة CredentialsByHostClient.CreateConnection. تُعيد هذه الطريقة كائنًا يطبق واجهة IDisposable. لإطلاق سراح الاتصال يجب استدعاء طريقة Dispose. يجب تنفيذ إنشاء وتدمير الاتصال داخل الخيط الذي تُجرى فيه عمليات البريد. محاولة إنشاء اتصال جديد في الخيط الذي تم إنشاء عميل البريد فيه يؤدي إلى خطأ، لأن هذا الخيط لا يمكنه في هذه اللحظة إنشاء اتصال جديد. كذلك لا يمكن إنشاء اتصال جديد عندما تكون القيمة MaxConnectionsPerServer = 1. مثال على شفرة إنشاء اتصال جديد لخيط إضافي:
توصيات
جدير بالذكر أنه إذا أرسل المستخدم جميع الأوامر إلى الاتصال الرئيسي، قد يحدث خلط بين الأوامر من خيوط مختلفة. يجب على المستخدم فهم أي الأوامر تعتمد على تسلسلها، واتخاذ إجراءات لمزامنة تلك الأوامر. كما يجب مراعاة إمكانية تنفيذ الأوامر في جلسات مختلفة (IMAP/POP3). أكثر العمليات استهلاكًا للوقت، مثل FetchMessage و AppendMessage و Send، ربما يكون من المنطقي تنفيذها في خيط جديد واتصال جديد. أما العمليات السريعة مثل Delete فمن الأفضل تنفيذها عبر الاتصال الرئيسي. يرجى ملاحظة أن إنشاء اتصال جديد يُعتبر عملية تستغرق وقتًا كبيرًا.