תמיכת מרובה‑תהליכים ומאגר חיבורים בלקוחות דואר

לקוחות דואר כגון ImapClient, Pop3Client, ו- SmtpClient ניתן להשתמש בו בסביבה מרובת‑תהליכים. לקוח יכול לשמור חיבור אחד או יותר עם שרת. לניהול קבוצת החיבורים בתוכו, משתמשים ב‑מאגר חיבורים. מספר החיבורים שניתן ליצור ולהשתמש בו במקביל מוגבל על‑ידי ה‑ CredentialsByHostClient.MaxConnectionsPerServer המאפיין. ניתן להגדיר מאפיין זה ל‑1 או ערך גבוה יותר. כברירת מחדל, הוא שווה ל‑10.

תור פקודות מיושם לכל חיבור כדי לתמוך בפעולות מרובות‑תהליכים. פקודות מממשות את הפעולות הפשוטות המוגדרות בפרוטוקול, כגון Noop, Authenticate, וכן הלאה. משתמש יכול להתחיל לבצע יותר פקודות ממה שיש חיבורים זמינים, אך הן יתבצעו רק כאשר הלקוח יוכל ליצור חיבור לפעולה.

איך לקוחות דואר מתנהגים בסביבה מרובת‑תהליכים

ללקוחות דוא"ל יש את ההתנהגות הבאה:

  1. כאשר MaxConnectionsPerServer = 1, הלקוח יוצר חיבור אחד ומבצע אימות והרשאה. חיבור זה נשמר במצב פעיל עד שהלקוח משוחרר. כל הפעולות מתהליכים שונים מופנות לתור פקודות אחד הממוקם בחיבור הראשי.

  2. כאשר 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 enumeration.

סוגי חיבורים

קיימים שלושה סוגי חיבורים:

  • החיבור הראשי. זהו החיבור שנוצר ומשוחרר יחד עם לקוח הדואר. אין אפשרות ליצור או לשחרר אותו ידנית.
  • חיבור ברירת‑מחדל. משתמש יכול ליצור חיבורים ברירת‑מחדל לתהליכים עם ה‑ 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 אובייקטים לתכנות מרובה‑משימות, מצב זה עלול לגרום להתנהגות שגויה. כדי למנוע זאת, על המשתמש לפנות ידנית את החיבור ברירת‑המחדל בסוף הרצת הקוד.

המלצות

אם המשתמש שולח את כל הפקודות לחיבור הראשי, ייתכן שתצבו שבו פקודות מתהליכים שונים מתערבבות. על המשתמש להבין אילו פקודות תלויות ברצף שלהן ולפעול לסנכרון פקודות אלו. כמו‑כן יש לקחת בחשבון אפשרות לביצוע פקודות ב‑sessions שונים (IMAP/POP3). הפעולות שלוקחות הכי הרבה זמן הן FetchMessage, AppendMessage, ו- Send. כנראה שפשוט לבצע את הפעולות הללו עם תהליך חדש וחיבור חדש. פעולות מהירות כגון Delete זה הגיוני לבצע עם החיבור הראשי. שים לב שהאתחול של חיבור חדש הוא פעולה שלוקחת זמן משמעותי.