دعم تعدد الخيوط ومجموعة الاتصالات في عملاء البريد
عملاء البريد مثل ImapClient, Pop3Client, و SmtpClient يمكن استخدامها في بيئة متعددة الخيوط. يمكن للعميل الاحتفاظ باتصال أو أكثر مع الخادم. لإدارة مجموعة الاتصالات داخل العميل، يُستخدم مجموعة الاتصالات. عدد الاتصالات التي يمكن إنشاؤها واستخدامها في نفس الوقت يحده الـ CredentialsByHostClient.MaxConnectionsPerServer خاصية. يمكن ضبط هذه الخاصية إلى 1 أو قيمة أعلى. بشكل افتراضي، تكون مساوية لـ 10.
يتم تنفيذ طابور أوامر لكل اتصال لدعم عمليات تعدد الخيوط. تُنفّذ الأوامر أبسط العمليات المحددة في البروتوكول، مثل Noop, Authenticate, وهكذا. قد يبدأ المستخدم تنفيذ أوامر أكثر مما هو متاح من اتصالات، لكن سيتم تنفيذها فقط عندما يتمكن العميل من إنشاء اتصال للعملية.
كيف يتصرف عملاء البريد في بيئة متعددة الخيوط
عملاء البريد الإلكتروني يتبعون السلوك التالي:
-
عند
MaxConnectionsPerServer = 1, ينشئ العميل اتصالًا واحدًا ويُجري المصادقة والتفويض. يبقى هذا الاتصال في حالة تشغيل حتى يُصرف العميل. تُوجيه جميع العمليات من خيوط مختلفة إلى طابور أوامر واحد موضَّع في الاتصال الرئيسي. -
عند
MaxConnectionsPerServer > 1, ينشئ العميل العدد المطلوب من الاتصالات ويُجري المصادقة والتفويض لكل اتصال. يُحجز اتصال واحد كالاتصال الرئيسي. يبقى هذا الاتصال في حالة تشغيل حتى يُصرف العميل. تُنشأ جميع الاتصالات الأخرى وتُصرف حسب الطلب. يُحدَّد الحد الأقصى لعدد هذه الاتصالات بواسطة الـMaxConnectionsPerServerخاصية. على سبيل المثال، إذاMaxConnectionsPerServer = 2, ثم يُحجز اتصال واحد كالاتصال الرئيسي، ويُستخدم اتصال ثانٍ كاتصال إضافي للعمليات التي تُنفَّذ في خيوط أخرى. وبالتالي، إذاMaxConnectionsPerServer = 3, ثم يُحجز الاتصال الأول كالاتصال الرئيسي، وتُستخدم اتصالان آخران كاتصالات إضافية للعمليات التي تُنفَّذ في خيوط أخرى. عندما يَطلب اتصال من خيط جديد وتكون جميع الاتصالات مشغولة، ينتظر العميل حتى يقل عدد الاتصالات المستخدمة. هذه لحظة مهمة جدًا توضح لماذا يعد تحرير الاتصالات بشكل صحيح مهمًا جدًا.
أمثلة على استخدام عملاء البريد في بيئة متعددة الخيوط
قد يُنفّذ المستخدم عمليات في خيوط مختلفة بعدة طرق. يمكن تقسيمها إلى نوعين.
استخدام الطرق غير المتزامنة (Begin/End)
يستخدم المستخدم الطرق غير المتزامنة (Begin/End) المحددة في العميل. في هذه الحالة، يطلق عميل البريد خيوطًا جديدة عند الحاجة. يتم تنفيذ طابور مهام في العميل (لا تُخلط مع طابور الأوامر في الاتصال). يمكن تنفيذ مهمة إذا كان الاتصال متاحًا. بمجرد أن يصبح عدد الاتصالات المستخدمة أقل من الحد المسموح، ينشئ العميل اتصالًا جديدًا، يُنشئ خيطًا للمهمة الحالية، وينفذ هذه المهمة. مثال على استخدام العمليات غير المتزامنة:
// Create an imapclient with host, user and password
ImapClient client = new ImapClient();
client.Host = "domain.com";
client.Username = "username";
client.Password = "password";
client.SelectFolder("InBox");
ImapMessageInfoCollection messages = client.ListMessages();
IAsyncResult res1 = client.BeginFetchMessage(messages[0].UniqueId);
IAsyncResult res2 = client.BeginFetchMessage(messages[1].UniqueId);
MailMessage msg1 = client.EndFetchMessage(res1);
MailMessage msg2 = client.EndFetchMessage(res2);
استخدام الخيوط التي ينشئها المستخدم
قد ينشئ المستخدم خيوطًا باستخدام كائنات مثل Thread, ThreadPool, Task, أو أي كائنات أخرى مخصصة لهذا الغرض. قد يستخدم المستخدم أيضًا خيوطًا تم إنشاؤها في شفرة طرف ثالث. في هذه الحالة، للعميل نموذج سلوكين.
أ. إذا لم يعتنِ المستخدم بإنشاء اتصالات إضافية للعمليات في الخيط، سيتم إرسال جميع عمليات هذا الخيط إلى طابور أوامر الاتصال الرئيسي. المثال التالي يُظهر عمليات في خيط إضافي دون إنشاء اتصال جديد — جميع المعاملات تُجرى عبر الاتصال الرئيسي:
List<MailMessage> List = new List<MailMessage>();
ThreadPool.QueueUserWorkItem(delegate(object o)
{
client.SelectFolder("folderName");
ImapMessageInfoCollection messageInfoCol = client.ListMessages();
foreach (ImapMessageInfo messageInfo in messageInfoCol)
{
List.Add(client.FetchMessage(messageInfo.UniqueId));
}
});
ب. عندما يقوم المستخدم بتشغيل طريقة لإنشاء اتصال جديد لخيط إضافي، يُحجز هذا الخيط حتى يتغير مقدار الحصة للاتصالات الجديدة ليسمح بإنشاء اتصال جديد. ثم يُنشأ اتصال جديد. يُضبط هذا الاتصال كالاتصال الافتراضي لجميع العمليات في هذا الخيط. بعد إكمال جميع العمليات في هذا الخيط، يجب تفريغ الاتصال. لإنشاء اتصالات جديدة، استخدم الـ CredentialsByHostClient.CreateConnection طريقة. تُعيد هذه الطريقة كائنًا يُطبق الـ IDisposable الواجهة. لتحرير الاتصال، الـ Dispose يجب استدعاء الطريقة. يجب إنشاء وتفريغ الاتصال داخل الخيط الذي تُنفَّذ فيه عمليات البريد. محاولة إنشاء اتصال جديد في الخيط الذي تم إنشاء عميل البريد فيه يؤدي إلى خطأ، لأن هذا الخيط لا يمكنه إنشاء اتصال جديد في تلك اللحظة. لا يمكن أيضًا إنشاء اتصال جديد عندما MaxConnectionsPerServer = 1. مثال شفرة لإنشاء اتصال جديد في خيط إضافي:
List<MailMessage> List1 = new List<MailMessage>();
ThreadPool.QueueUserWorkItem(delegate(object o)
{
using (IDisposable connection = client.CreateConnection())
{
client.SelectFolder("FolderName");
ImapMessageInfoCollection messageInfoCol = client.ListMessages();
foreach (ImapMessageInfo messageInfo in messageInfoCol)
List1.Add(client.FetchMessage(messageInfo.UniqueId));
}
});
مجموعة الاتصالات
بدءًا من Aspose.Email 19.3، تم إعادة هيكلة مجموعة الاتصالات. الـ EmailClient تم إدخال الفئة، التي تستبدل في النهاية الـ CredentialsByHostClient الفئة. الـ EmailClient الصف يوفر ConnectionAsgmtMode الخاصية التي تحدد وضع تخصيص الاتصال في بيئة متعددة الخيوط. EmailClient.ConnectionAsgmtMode يتم تعيينه باستخدام الـ ConnectionAsgmtType عددية.
أنواع الاتصالات
هناك ثلاثة أنواع من الاتصالات:
- الاتصال الرئيسي. هذا هو الاتصال الذي يُنشأ ويُصرف مع عميل البريد. لا يمكن إنشاؤه أو صرفه يدويًا.
- الاتصال الافتراضي. يمكن للمستخدم إنشاء اتصالات افتراضية للخيوط باستخدام CreateConnection طريقة. إذا كان هناك اتصال افتراضي، ستستخدم جميع طرق عميل البريد المنفذة في خيط ما هذا الاتصال ضمنيًا. لا يمكن وجود أكثر من اتصال افتراضي واحد لكل خيط. يمكن إنشاؤه يدويًا أو تلقائيًا، حسب الـ
EmailClient.ConnectionAsgmtModeخاصية. يمكن إنشاء هذه الاتصالات يدويًا باستخدامEmailClient.CreateConnection(createAsDefaultConnection = true)طريقة. إذا لم تُستَخدم اتصال افتراضي (يعتمد على وضع تخصيص الاتصال)، يُستخدم الاتصال الرئيسي ضمنيًا بدلاً منه. - اتصالات مستقلة. هذه هي الاتصالات التي لا ترتبط بخيوط. يمكن إنشاؤها يدويًا ويجب استخدامها صراحةً كمعامل للطريقة. يمكن إنشاء هذه الاتصالات يدويًا باستخدام
EmailClient.CreateConnection()طريقة أو الـEmailClient.CreateConnection(createAsDefaultConnection = false)طريقة.
أنواع تخصيص الاتصال
لإعداد الـ EmailClient.ConnectionAsgmtMode خاصية، الـ ConnectionAsgmtType التعداد يُستخدم. أنواع التخصيص التي يوفرها مذكورة أدناه.
-
ConnectionAsgmtType.UseMainOrDefault يُستخدم هذا الوضع افتراضيًا في عملاء البريد. يستخدم عميل البريد الاتصال الرئيسي لجميع العمليات من خيوط متعددة إذا لم يتم إنشاء اتصال افتراضي، أو إذا لم يتم تمرير اتصال كمعامل للطريقة صراحةً. يُنشأ الاتصال الرئيسي في نفس وقت إنشاء عميل البريد. يمكن للمستخدم إنشاء اتصالات افتراضية للخيوط باستخدام
CreateConnectionطريقة. إذا تم إنشاء اتصال افتراضي لخيط، يُستخدم ضمنيًا لجميع طرق عميل البريد المستدعاة في ذلك الخيط. إذا لم يتم إنشاء اتصال افتراضي لخيط، يُستخدم الاتصال الرئيسي لجميع الطرق المستدعاة في ذلك الخيط. يمكن للمستخدم أيضًا إنشاء اتصالات غير مرتبطة بخيوط (ليست اتصالات افتراضية) باستخدامCreateConnectionطريقة. لاستخدام اتصالات أخرى (ليس الرئيسي ولا الافتراضي)، يجب على المستخدم تمرير الاتصال صراحةً كمعامل للطريقة. يمكن للمستخدم أيضًا إنشاء عدد غير محدود من الاتصالات. لا يمكن أن يوجد أكثر من اتصال افتراضي واحد لكل خيط. يرجى ملاحظة أن الاتصالات الافتراضية تعمل بشكل صحيح إذا استخدم المستخدمThreadالكائنات لبرمجة المهام المتعددة. إذا استخدم المستخدم مجموعة اتصالات أوTaskالكائنات للبرمجة المتعددة المهام، قد يؤدي هذا الوضع إلى سلوك غير صحيح. لتجنب هذه المشكلة، يجب على المستخدم التخلص يدويًا من الاتصال الافتراضي (إذا استُخدم) في نهاية تنفيذ الشيفرة. -
ConnectionAsgmtType.UseMain يستخدم عميل البريد الاتصال الرئيسي لجميع العمليات من خيوط متعددة. يُنشأ الاتصال الرئيسي في نفس وقت إنشاء عميل البريد. لا يمكن للمستخدم إنشاء اتصالات افتراضية، لكنه يمكنه إنشاء اتصالات غير مرتبطة بخيوط باستخدام
CreateConnectionطريقة. لاستخدام اتصالات أخرى، يجب على المستخدم تمريرها صراحةً كمعامل للطريقة. -
ConnectionAsgmtType.UseDefault يستخدم عميل البريد الاتصالات الافتراضية فقط ضمنيًا لجميع العمليات من خيوط متعددة. لا يُستخدم الاتصال الرئيسي في هذا الوضع. إذا لم يتم إنشاء اتصال افتراضي لخيط ما (عند أول استدعاء لطريقة في عميل البريد)، ينشئ عميل البريد اتصالًا افتراضيًا ضمنيًا لهذا الخيط قبل تنفيذ العملية الأولى. لا يمكن للمستخدم إنشاء اتصالات افتراضية للخيوط باستخدام
CreateConnectionطريقة لأنها تُنشأ تلقائيًا. يمكن للمستخدم أيضًا إنشاء اتصالات غير مرتبطة بخيوط باستخدامCreateConnectionطريقة. لاستخدام اتصالات أخرى، يجب على المستخدم تمريرها صراحةً كمعامل للطريقة. يمكن للمستخدم أيضًا إنشاء عدد غير محدود من الاتصالات. لا يمكن استخدام أكثر من اتصال افتراضي واحد لكل خيط. يرجى ملاحظة أن الاتصالات الافتراضية تعمل بشكل صحيح إذا استخدم المستخدمThreadالكائنات لبرمجة المهام المتعددة. إذا استخدم المستخدم مجموعة اتصالات أوTaskالكائنات للبرمجة المتعددة المهام، قد يؤدي هذا الوضع إلى سلوك غير صحيح. لتجنب هذه المشكلة، يتوجب على المستخدم التخلص يدويًا من الاتصال الافتراضي في نهاية تنفيذ الشيفرة.
توصيات
إذا أرسل المستخدم جميع الأوامر إلى الاتصال الرئيسي، قد يحدث خلط بين الأوامر القادمة من خيوط مختلفة. يجب على المستخدم فهم أي الأوامر تعتمد على تسلسلها واتخاذ إجراءات لمزامنة هذه الأوامر. كما يلزم أخذ احتمال تنفيذ الأوامر في جلسات مختلفة (IMAP/POP3) بعين الاعتبار. أكثر العمليات استهلاكًا للوقت هي FetchMessage, AppendMessage, و Send. قد يكون من المنطقي إجراء هذه العمليات بخيط جديد واتصال جديد. العمليات السريعة مثل Delete من المنطقي إجراء ذلك باستخدام الاتصال الرئيسي. يرجى ملاحظة أن تهيئة اتصال جديد هي عملية تستغرق وقتًا كبيرًا.