עבודה ב-Multithreading באפליקציות GUI – חלק 1

עבודה ב-Multithreading באפליקציות GUI – חלק 1

Print Friendly, PDF & Email
בעבודה עם GUI בתכנות מרובה נימים (Threads) ישנם נושאים שבדרך כלל הם טריוויאלים אך מעצם העבודה באופן כזה נדרשת טכניקה מיוחדת להתמודדות עם בעיות ייחודיות למצב זה. בפוסט זה אפרט חלק מהנושאים הבעייתיים ואת אופני הפתרון המומלצים.

עדכון אובייקט גרפי מ-Thread אחר מזה שיצר אותו: מפתחים רבים נתקלים בבעיה בעדכון אובייקטי GUI מתוך Threads משום שה-Thread שיצר את האובייקט הגרפי (למשל Progress Bar) הוא לא ה-Thread שמבצע את העבודה ואשר מסוגל לספק את הנתון לגבי התקדמות העבודה (הודעת "Cross thread operation not valid"). הפתרון הוא שימוש בטכניקת Invoke באופן הבא:
ראשית, מגדירים delegate שישמש אח"כ להעברת הכתובת של המתודה שיש להריץ ע"י Invoke לצורך עדכון האובייקט הגרפי. נוכל להגדיר כל delegate שנרצה.
//This delegate will be used to call the method that will
//actually change the text in the textbox.
delegate void SafelySetTextBox_Handler(string Text);
כעת, יש להגדיר מתודה שהיא זו שתבצע את העבודה (למשל, חיפוש אחר קובץ) והיא תהווה את ה-Thread הנפרד שלמעשה נדרש לעדכן אובייקט גרפי שלא הוא יצר.
//This method makes the thread that will do the work and
//will have to update the textbox in order to notify the
//user about the progress.

public void DoAction()
{
  for (int i=0; i<10; i++)
  {
    SafelySetTextBox(i);
  }
}


המתודה הבאה היא המתודה שמזניקה את ה-Thread שיבצע את העבודה (למשל חיפוש אחר קובץ). כמובן שזו יכולה להיות כל מתודה במערכת, מתודת OnClick של כפתור, OnLoad של טופס או כל דבר אחר (למשל Elapsed של אובייקט Timer) במקרה זה בחרתי להזניק את ה-Thread ישירות מתוך ה-OnLoad של הטופס, כך:
//This method will launch the thread that will do the main process (i.e search for files, etc.)

public void Form1_FormLoad(object sender, eventargs e)
{
   Thread t1 = new Thread(new ThreadStart(DoAction));
   t1.Start();
}

מתודה זו היא המתודה שמעדכנת את הטקסט המבוקש לתיבת הטקסט. משום שלא ניתן לספק למתודה Invoke את הפקודה להצבת הערך לתיבת הטקסט, יש ליצור מתודה המבצעת זאת ואת כתובתה להעביר למתודת ה-Invoke במידת הצורך
//This method will format the text and set it to the textbox directly.

private void SetTextBox(string Text)
{
   MyTextBox.Text = Text;
}

מתודה זו היא המתודה שיש לקרוא לה כאשר מעוניינים לשנות את הטקסט ב- TextBox. היא משתמשת במאפיין InvokeRequired כדי לקבוע כיצד יש לפנות למתודה SetTextBox לצורך ביצוע השינוי בפועל. למעשה לא היינו חייבים את המתודה הזו, היה ניתן לשים את התנאי הזה בכל מקום בקוד בו רצינו לשנות את הטקסט ב-TextBox, מטעמי נוחות וכתיבה נכונה של קוד הגדרתי את המתודה הזו. מתודת Invoke מקבלת 2 ארגומנטים: הראשון הוא ה-delegate אשר מגדיר למעשה את הפונקציה שיש להפעיל, השני הוא מערך מטיפוס object שמכיל את הארגומנטים שיש לשלוח למתודה המוזנקת בסדר שיש לשלוח אותם.

//This method decides how to set the text to the textbox.
//This method is called from the thread that didn't create the
//textbox.

private void SafelySetTextBox(string Text)
{
   if (MyTextBox.InvokeRequired)
   {
      MyTextBox.Invoke(
               new SafelySetTextBox_Handler(SetTextBox),
               new object[]{Text});
   }
   else
   {
      SetTextBox(Text);
   }
}

השאר תגובה