מתי להשתמש ב- Task.WaitAll לעומת Task.WhenAll ב- .NET

TPL (Task Parallel Library) היא אחת התכונות החדשות המעניינות שנוספו בגרסאות האחרונות של .NET framework. השיטות Task.WaitAll ו- Task.WhenAll הן שתי שיטות חשובות ונמצאות בשימוש תכוף ב- TPL.

ה- Task.WaitAll חוסם את השרשור הנוכחי עד שכל שאר המשימות השלימו את הביצוע. השיטה Task.WhenAll משמשת ליצירת משימה שתושלם אם ורק אם כל המשימות האחרות הושלמו.

לכן, אם אתה משתמש ב- Task.WhenAll תקבל אובייקט משימה שאינו שלם. עם זאת, היא לא תחסום אך תאפשר לבצע את התוכנית. נהפוך הוא, שיחת השיטה Task.WaitAll למעשה חוסמת ומחכה לביצוע כל המשימות האחרות.

בעיקרו של דבר, Task.WhenAll ייתן לך משימה שלא הושלמה, אך תוכל להשתמש ב- ContinueWith ברגע שהמשימות שצוינו סיימו את ביצוען. שים לב שלא Task.WhenAll וגם Task.WaitAll לא יפעילו את המשימות; כלומר, אין משימות שמתחילות בשיטות אלה. כך משתמשים ב- ContinueWith עם Task.WhenAll: 

Task.WhenAll (רשימת המשימות) .ContinueWith (t => {

  // כתוב כאן את הקוד שלך

});

כפי שקובע התיעוד של מיקרוסופט, Task.WhenAll "יוצר משימה שתושלם כשכל אובייקטים המשימות באוסף אינספור הושלמו."

Task.WhenAll לעומת Task.WaitAll

תן לי להסביר את ההבדל בין שתי השיטות הללו בדוגמה פשוטה. נניח שיש לך משימה שמבצעת פעילות כלשהי באמצעות שרשור ממשק המשתמש - נניח, אנימציה כלשהי צריכה להיות מוצגת בממשק המשתמש. כעת, אם אתה משתמש ב- Task.WaitAll, ממשק המשתמש ייחסם ולא יעודכן עד לסיום כל המשימות הקשורות ושחרור החסימה. עם זאת, אם אתה משתמש ב- Task.WhenAll באותה יישום, שרשור ממשק המשתמש לא ייחסם והוא יעודכן כרגיל.

אז באיזו מהשיטות הללו עליכם להשתמש מתי? ובכן, אתה יכול להשתמש ב- WaitAll כאשר הכוונה חוסמת באופן סינכרוני כדי להשיג את התוצאות. אך כאשר תרצה למנף אסינכרוניה, תרצה להשתמש בגרסת WhenAll. אתה יכול לחכות ל- Task.WhenAll מבלי שתצטרך לחסום את השרשור הנוכחי. לפיכך, ייתכן שתרצה להשתמש ב- wait עם Task.WhenAll בתוך שיטת async.

בעוד ש- Task.WaitAll חוסם את השרשור הנוכחי עד לסיום כל המשימות הממתינות, Task.WhenAll מחזיר אובייקט משימה. Task.WaitAll זורק AggregateException כאשר אחת או יותר מהמשימות מביאות חריג. כאשר מטלה אחת או יותר מביאות חריג ואתה ממתין לשיטת Task.WhenAll, היא עוטפת את ה- AggregateException ומחזירה רק את הראשונה.

הימנע משימוש ב- Task. הפעל בלולאות

אתה יכול להשתמש במשימות כאשר ברצונך לבצע פעילויות במקביל. אם אתה זקוק למדרגה גבוהה של מקבילות, משימות אינן בחירה טובה. תמיד מומלץ להימנע משימוש בשרשור מאגרי חוטים ב- ASP.Net. לפיכך, עליך להימנע משימוש ב- Task.Run או Task.factory.StartNew ב- ASP.Net.

Task.Run תמיד צריך לשמש לקוד מאוגד למעבד. Task.Run אינו בחירה טובה ביישומי ASP.Net, או ביישומים המנצלים את זמן הריצה של ASP.Net מכיוון שהוא פשוט מוריד את העבודה לשרשור ThreadPool. אם אתה משתמש ב- API של ASP.Net Web, הבקשה כבר משתמשת בשרשור ThreadPool. לפיכך, אם אתה משתמש ב- Task.Run ביישום ה- ASP.Net Web API שלך, אתה רק מגביל את יכולת ההרחבה על ידי הורדת העבודה לשרשור עובד אחר ללא כל סיבה.

שים לב שיש חסרון בשימוש ב- Task.Run בלולאה. אם אתה משתמש בשיטת Task.Run בתוך לולאה, ייווצרו משימות מרובות - אחת לכל יחידת עבודה או איטרציה. עם זאת, אם אתה משתמש ב- Parallel.ForEach במקום להשתמש ב- Task.Run בתוך לולאה, מחלק נוצר כדי להימנע מיצירת משימות רבות יותר לביצוע הפעילות ממה שהיא נחוצה. זה עשוי לשפר את הביצועים באופן משמעותי מכיוון שתוכל להימנע ממתגי הקשר רבים מדי ועדיין למנף מספר ליבות במערכת שלך.

יש לציין כי Parallel.ForEach משתמשת ב- Partitioner באופן פנימי כדי להפיץ את האוסף לפריטי עבודה. אגב, הפצה זו אינה מתרחשת עבור כל משימה ברשימת הפריטים, אלא היא מתרחשת כמנה. זה מוריד את התקורה הכרוכה בכך ומשפר את הביצועים. במילים אחרות, אם אתה משתמש ב- Task.Run או Task.Factory.StartNew בתוך לולאה, הם ייצרו משימות חדשות באופן מפורש עבור כל איטרציה בלולאה. Parallel.ForEach יעיל הרבה יותר מכיוון שהוא ייעל את הביצוע על ידי חלוקת עומס העבודה על פני מספר הליבות במערכת שלך.