ניהול משימות אסינכרוני ומקבילות
עבור מפתחי C# , ספריית המשימות המקבילה (TPL) מספקת דרך קלה יותר לכתוב קוד מקבילי. עם זאת, לא כל הקוד מתאים ל מקביליות. לדוגמה, אם לולאה מבצעת רק כמות קטנה של עבודה בכל אחת מהפעולות האיטראציות, או שהיא אינה פועלת עבור מספר רב של דרגות, התמורת של המקבילות עלולה לגרום לקוד לפעול לאט יותר. יתר על כן, מקביליות, כמו כל קוד מרובה משתתפים, מוסיפה מורכבות לביצוע התוכנית שלך.
שגיאות נפוצות במקבילות בנתונים ובפעילויות
במקרים רבים, והוא Parallel.For יכול Parallel.ForEach לספק שיפורי ביצועים משמעותיים בלולאות רציפות רגילות. עם זאת, העבודה של מקבילי הלולאה מציגה מורכבות שעלולה להוביל לבעיות שאינן נפוצות בקוד רציף.
אל תניח שהמקביל תמיד מהיר יותר
במקרים מסוימים, לולאה מקבילה עשויה לפעול לאט יותר מהמקבילה הרציונית שלה. כלל האגודל הבסיסי הוא שלולאות מקבילות הכוללות מספר איתציות ונציגי משתמשים מהירים אינם כוללים סבירות גבוהה לזרז את המהירות. עם זאת, מאחר שגורמים רבים מעורבים בביצועים, מומלץ למדוד תמיד תוצאות בפועל.
הימנע מכתיבה במיקומים משותפים בזיכרון
בקוד רציף, לא נדיר לקרוא או לכתוב למשתנים סטטיים או לשדות מחלקה. עם זאת, בכל פעם שרשורים מרובים ניגשים למשתנים אלה בו-זמנית, יש פוטנציאל משמעותי לתנאי מירוץ. למרות שניתן להשתמש בנעילה כדי לסנכרן את הגישה למשתנה, עלות הסינכרון יכולה לפגוע בביצועים. לכן, מומלץ להימנע, או לפחות להגביל, גישה למצב משותף בלולאה מקבילה ככל האפשר. הדרך הטובה ביותר לעשות זאת היא להשתמש בעומס Parallel.ForParallel.ForEachSystem.Threading.ThreadLocal<T> יתר של ולהשתמש במשתנה לאחסון מצב מקומי של הליך משנה במהלך ביצוע לולאה.
הימנעות ממתן מקבילות
על-ידי שימוש בלולאות מקבילות, אתה צבור את עלויות התקורה של חלוקת אוסף המקור למחיצות וסינכרון הליכי המשנה של רכיב עובד. היתרונות של המקבילות מוגבלים עוד יותר על-ידי מספר המעבדים במחשב. לא ניתן להשיג מהירות על-ידי הפעלת הליכי משנה מרובים של חישוב מאוגד במעבד אחד בלבד. לכן, עליך להיזהר לא לבצע מעבר מקבילית בלולאה.
התרחיש הנפוץ ביותר שבו מקביליות יתר עשויה להתרחש הוא בלולאות מקוננות. ברוב המקרים, מומלץ לבצע מקבילות של הלולאה העליונה בלבד, אלא אם חל אחד או יותר מהתנאים הבאים:
- הלולאה הפנימית ידועה כ'ארוכה'.
- אתה מבצע חישוב יקר בכל הזמנה.
- ידוע שלמערכת היעד יש מספיק מעבדים לטיפול במספר הליכי המשנה המופקים על-ידי מקבילי העיבוד.
בכל המקרים, הדרך הטובה ביותר לקבוע את צורת השאילתה המיטבית היא לבדוק ולמדוד.
טיפול בחריגים במשימות אסינכרונית ומקבילות
בעת שימוש ב- Task Parallel Library (TPL) להפעלת משימות, חריגים יכולים להתרחש במספר דרכים שונות. התדירות הנפוצה ביותר היא כאשר פעילות מתנויעה על חריגה. התריעה על חריג עשויה להתרחש כאשר המשימה פועלת בהליך משנה של מאגר הליכי משנה או כאשר היא פועלת בשרשור הראשי. בכל מקרה, החריגה מופצת בחזרה להליך המשנה של השיחות.
בעת שימוש בפעולת Task.Wait השירות כדי להמתין להשלמת משימה, כל החריגים שהושלמו על-ידי המשימה מופצים בחזרה לרצף התגובות של השיחות. באפשרותך לטפל בחריגים אלה באמצעות בלוק ניסיון/קליט. אם פעילות היא האב של משימות צאצא מצורפות, או אם אתה ממתין לפעילויות מרובות, ייתכן שתזרקה חריגות מרובות. אם מתרחשת חריגה אחת או יותר, הן גולשות במופע AggregateException .
לחריגה AggregateException יש InnerExceptions מאפיין שניתן לספור כדי לבחון את כל החריגים המקוריים שהושלכו, ולטפל (או לא לטפל) בכל אחד בנפרד.
הדוגמה הבאה מדגימה כיצד לטפל בחריגים שמשימה זרקה.
public static partial class Program
{
public static void Main()
{
HandleThree();
}
public static void HandleThree()
{
var task = Task.Run(
() => throw new CustomException("This exception is expected!"));
try
{
task.Wait();
}
catch (AggregateException ae)
{
foreach (var ex in ae.InnerExceptions)
{
// Handle the custom exception.
if (ex is CustomException)
{
Console.WriteLine(ex.Message);
}
// Rethrow any other exception.
else
{
throw ex;
}
}
}
}
}
// Define the CustomException class
public class CustomException : Exception
{
public CustomException(string message) : base(message) { }
}
// The example displays the following output:
// This exception is expected!
בדוגמה זו, HandleThree פעולת השירות יוצרת משימה שגורמת ל- CustomException. הבלוק try/catch תופס את האוסף AggregateException ואת הפריטים שהוא עובר InnerExceptions עליו. אם החריגה היא מסוג , היא CustomExceptionמדפיסה את ההודעה במסוף. אם זהו סוג אחר של חריגה, הוא מחדש אותו.
באפשרותך גם לטפל בחריגים המקוריים באמצעות פעולת AggregateException.Handle השירות. שיטה זו מקבלת נציג שנקרא עבור כל חריגה באוסף InnerExceptions . אם הנציג מחזיר ערך True, החריגה נחשבת למטפלת ו מוסרת מהאוסף. אם הוא מחזיר False, החריגה מתרחשת מחדש.
הדוגמה הבאה מדגימה כיצד להשתמש בפעולת Handle השירות לטיפול בחריגים שמשימה זרקה.
public static partial class Program
{
public static void HandleFour()
{
var task = Task.Run(
() => throw new CustomException("This exception is expected!"));
try
{
task.Wait();
}
catch (AggregateException ae)
{
ae.Handle(ex =>
{
// Handle the custom exception.
if (ex is CustomException)
{
Console.WriteLine(ex.Message);
return true;
}
// Rethrow any other exception.
return false;
});
}
}
}
בדוגמה זו, HandleFour פעולת השירות יוצרת משימה שגורמת ל- CustomException. הבלוק try/catch תופס את פעולת AggregateException השירות ואת קורא Handle לה. הנציג בודק אם החריגה היא מסוג CustomException. אם החריגה היא מסוג , הנציג CustomExceptionמדפיס את ההודעה במסוף ומחזיר true. תגובה המציינת true כי החריגה טופלה. אם החריגה היא סוג אחר של חריגה, הנציג מחזיר falseאת , וגורם לחריגה להופיע מחדש.
תקציר
יחידה זו מתארת מצבים שבהם קוד אינו מתאים ל מקביליות ודיון בשגיאות נפוצות בנתונים ובמקביליזם של משימות. לדוגמה, בהנחה שהמקביל הוא תמיד מהיר יותר, כתיבה אל מיקומי זיכרון משותפים והתאמה מקבילית מדי. התוכן מסביר גם כיצד לטפל בחריגות במשימות אסינכרונית ומקבילות, כולל אופן השימוש בפעולת Task.Wait השירות ובשיטה AggregateException.Handle .
נקודות עיקריות
- לא כל הקוד מתאים ל מקביליות. בדיקה ומדידה של הביצועים חיוניים לפני ביצוע מקבילי של קוד.
- שגיאות נפוצות בנתונים ובמקביליזם של משימות כוללות בהנחה שהמקבילות היא תמיד מהירה יותר, כתיבה למיקומים משותפים בזיכרון ומקבילות מדי.
- ניתן לטפל בחריגות במשימות אסינכרונית ובמשימות מקבילות באמצעות
Task.Waitפעולת השירות והשיטהAggregateException.Handle. - לחריגה
AggregateExceptionישInnerExceptionsמאפיין שניתן לספור כדי לבחון את כל החריגים המקוריים שהושלכו.