דילוג לתוכן
  • דף הבית
  • קטגוריות
  • פוסטים אחרונים
  • משתמשים
  • חיפוש
  • חוקי הפורום
כיווץ
תחומים

תחומים - פורום חרדי מקצועי

💡 רוצה לזכור קריאת שמע בזמן? לחץ כאן!
  1. דף הבית
  2. תכנות
  3. שימוש נכון ב-await Task.Run ב-C#

שימוש נכון ב-await Task.Run ב-C#

מתוזמן נעוץ נעול הועבר תכנות
7 פוסטים 3 כותבים 322 צפיות
  • מהישן לחדש
  • מהחדש לישן
  • הכי הרבה הצבעות
התחברו כדי לפרסם תגובה
נושא זה נמחק. רק משתמשים עם הרשאות מתאימות יוכלו לצפות בו.
  • pcinfogmachP מנותק
    pcinfogmachP מנותק
    pcinfogmach
    כתב ב נערך לאחרונה על ידי pcinfogmach
    #1

    אשמח אם מישהו יסביר לי קצת יותר מתי ואיך נכון להשתמש ב-await Task.Run.
    כמו"כ קראתי כמה פעמים על משהו שנקרא תנאי תחרות (Race Condition) כשמשתמשים ב-Multithreading - אני חייב לומר שלא בדיוק הבנתי את הנושא.
    לעת עתה כל מה שאני יודע שזה נותן לי אפשרות הריץ קוד בלי לחסום את ה-UI. או להריץ כמה דברים בו זמנית (שזה בעצם אותו עיקרון).

    שאלותיי הם:
    מהם ההדרכות לכתיבת קוד אסינכרוני ב-C# וממה יש להיזהר?

    דוגמת קוד:
    האם קוד זה טוב או בעייתי או טעון שיפור? אשמח גם לקבל הסבר מדוע:

    async Task LoadCommentries(IOrderedEnumerable<LinkItem> commentryList, int lineIndex)
    {
        Commentry = await Task.Run(async () =>
        {
            var stringBuilder = new StringBuilder();
            foreach (var commentry in commentryList)
            {
                stringBuilder.AppendLine(await GetCommentryLine(commentry.path_2, lineIndex));
            }
            return stringBuilder.ToString();
        });
    }
    
    async Task<string> GetCommentryLine(string path, int lineIndex)
    {
        using (var reader = new StreamReader(path))
        {
            int currentIndex = 0;
            while (!reader.EndOfStream && currentIndex < lineIndex)
            {
                await reader.ReadLineAsync();
                currentIndex++;
            }
            return await reader.ReadLineAsync();
        }
    }
    

    גמ"ח מידע מחשבים ואופיס

    קומפיונטק 2 תגובות תגובה אחרונה
    0
    • קומפיונטק מנותק
      קומפיונטק מנותק
      קומפיונט
      השיב לpcinfogmach ב נערך לאחרונה על ידי קומפיונט
      #2

      @pcinfogmach כתב בשימוש נכון ב-await Task.Run ב-C#:

      מהם ההדרכות לכתיבת קוד אסינכרוני ב-C# וממה יש להיזהר?

      זה נושא מורכב שהרבה מסתבכים איתו ועושים טעויות, אני ממליץ לך לעבור על המסמך AsyncGuidance שהתודעתי אליו בעבר מהפורום.

      תגובה 1 תגובה אחרונה
      2
      • dovidD מחובר
        dovidD מחובר
        dovid ניהול
        כתב ב נערך לאחרונה על ידי dovid
        #3

        מתי: אף פעם. כשצריך, תדע את זה. למשל אם פעולה גורמת לUI לקפוא - אמור להיות נדיר, כי IO יש כבר Task מובנה, ובCPU, צריך לעשות ממש דברים רציניים מאוד כדי שתהיה הצדקה לקפיאה.

        יש שני סיבות למה להשתמש בכלל בTask:
        א. לומר לתוכנה שהיא לא צריכה הרגע לחכות לתוצאה, אלא לכשתגיע נדבר.
        ב. לנצל את ריבוי המעבדים.

        כשאתה מבצע קריאות מדיסק, המעבד לא עובד בכלל. אתה לא רוצה לעשות את זה במקביל, אתה סה"כ רוצה שהתוכנה תדע שאפשר בינתיים ללחוץ על כפתור אחר, או לגלול תצוגה וכדומה עד הסיום. זה נקרא פעולות מסוג IO.
        בכל פעולות הIO הנפוצות יש כיום מתודה מובנית שמחזירה Task - הרעיון הוא להריץ פקודה, ולקבל כביכול להודעה שזה הסתיים.
        בפונקציה הראשונה שלך, מה שלוקח זמן זה המתודה GetCommentryLine, והיא כבר Task. בגלל שיש בstringBuilder.AppendLine את הawait, בעצם מה שקורה זה שאתה אומר לGetCommentryLine: כשאת גומרת אנא הכניסי את השורה שיצא לתוך הstringBuilder, ואז תשלחי לביצוע את הcommentry הבא. זה מעולה בלי שום עטיפה של Task מיותר על הפעולה הזו.
        הנה איך שהייתי כותב:

        async Task<string> LoadCommentris(IOrderedEnumerable<LinkItem> commentryList, int lineIndex)
        {
            StringBuilder stringBuilder = new StringBuilder();
            foreach (var commentry in commentryList)
            {
                stringBuilder.AppendLine(await GetCommentryLine(commentry.path_2, lineIndex));
            }
            return stringBuilder.ToString();
        }
        

        את הפונקציה השניה הייתי משתמש בכלים המובנים יותר כיום:

        async Task<string> GetCommentryLine(string path, int lineIndex)
        {
            var index = 0;
            await foreach (var line in File.ReadLinesAsync(path))
                if(index++ == lineIndex)
                    return line;
            return null;
        }
        

        מנטור אישי למתכנתים (ולא רק) – להתקדם לשלב הבא!

        בכל נושא אפשר ליצור קשר dovid@tchumim.com

        קומפיונטק 2 תגובות תגובה אחרונה
        5
        • קומפיונטק מנותק
          קומפיונטק מנותק
          קומפיונט
          השיב לpcinfogmach ב נערך לאחרונה על ידי קומפיונט
          #4

          @pcinfogmach כתב בשימוש נכון ב-await Task.Run ב-C#:

          כמו"כ קראתי כמה פעמים על משהו שנקרא תנאי תחרות (Race Condition) כשמשתמשים ב-Multithreading - אני חייב לומר שלא בדיוק הבנתי את הנושא.

          קח את הקוד הבא:

          var count = 0;
          
          void Increment()
          {
              for (var i = 0; i < 1_000_000; i++)
              {
                  count++;
              }
          }
          
          var task1 = Task.Run(Increment);
          var task2 = Task.Run(Increment);
          
          await Task.WhenAll([task1, task2]);
          
          Console.WriteLine(count);
          

          הקוד הזה מריץ במקביל שני פונקיות שמקדמות את count במיליון, אם תצפה ש count יהיה שווה לשני מליון אחרי ההרצה, אז אתה תתפלא לראות שבכל הרצה מודפס ערך שונה, ואף פעם לא שני מליון.

          הסיבה לזה טמונה לדרך הפעולות של המעבד. הקוד ++count נראה פשוט אבל מורכב מכמה פעולות:
          א. קריאת הערך של count
          ב. חישוב הערך החדש
          ג. כתיבת הערך החדש ל - count

          בגלל שהפעולות מתבצעות במקביל אז כשהמעבד עובר בין ה - threadים (בתהליך שנקרא context-switch) זה גם יכול לקרות באמצע הפעולות הנ"ל, לדוגמא אחרי פעולות א' ו-ב' המעבד עובר ל - thread השני ואז התוצאה היא שאיבדנו קידום של ספרה אחת.

          אם תשנה את שורה 7 לזה:

          Interlocked.Increment(ref count);
          

          או לזה:

          lock(...)
          {
              count++;
          }
          

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

          אני יודע שזה מסובך אבל ניסיתי להסביר כמה שהתאפשר לי.

          תגובה 1 תגובה אחרונה
          4
          • קומפיונטק מנותק
            קומפיונטק מנותק
            קומפיונט
            השיב לdovid ב נערך לאחרונה על ידי קומפיונט
            #5

            @dovid כתב בשימוש נכון ב-await Task.Run ב-C#:

            את הפונקציה השניה הייתי משתמש בכלים המובנים יותר כיום:

            לכאורה הוא השתמש בשיטה שלו כדי למנוע את טעינת יתר השורות לזכרון.

            עריכה: אני לא צודק, שימוש ב - ReadLinesAsync לא יטען את שאר השורות.

            תגובה 1 תגובה אחרונה
            3
            • dovidD מחובר
              dovidD מחובר
              dovid ניהול
              כתב ב נערך לאחרונה על ידי
              #6

              @קומפיונט זה נכון בReadAllLines.

              מנטור אישי למתכנתים (ולא רק) – להתקדם לשלב הבא!

              בכל נושא אפשר ליצור קשר dovid@tchumim.com

              תגובה 1 תגובה אחרונה
              2
              • קומפיונטק מנותק
                קומפיונטק מנותק
                קומפיונט
                השיב לdovid ב נערך לאחרונה על ידי קומפיונט
                #7

                @dovid כתב בשימוש נכון ב-await Task.Run ב-C#:

                ובCPU, צריך לעשות ממש דברים רציניים מאוד כדי שתהיה הצדקה לקפיאה.

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

                יש שני סיבות למה להשתמש בכלל בTask:
                א. לומר לתוכנה שהיא לא צריכה הרגע לחכות לתוצאה, אלא לכשתגיע נדבר.
                ב. לנצל את ריבוי המעבדים.

                הייתי מגדיר את זה טיפה שונה: המטרה העיקרית של Task ו - async/await היא לא לחסום את ה - thread הראשי. המושג הכללי multi-threading נועד גם בין היתר לנצל את ריבוי המעבדים.

                תגובה 1 תגובה אחרונה
                1

                בא תתחבר לדף היומי!
                • התחברות

                • אין לך חשבון עדיין? הרשמה

                • התחברו או הירשמו כדי לחפש.
                • פוסט ראשון
                  פוסט אחרון
                0
                • דף הבית
                • קטגוריות
                • פוסטים אחרונים
                • משתמשים
                • חיפוש
                • חוקי הפורום