מאמר שהוגש על ידי מרקוס יונגלס
בעת תכנות מטפל באירועים בדלפי (כמו ה- בלחיצה במקרה של כפתור TB), מגיע הזמן בו היישום שלך צריך להיות עסוק לזמן מה, למשל הקוד צריך לכתוב קובץ גדול או לדחוס נתונים מסוימים.
אם תעשה זאת תבחין בכך נראה שהיישום שלך נעול. לא ניתן להזיז את הטופס שלך יותר והכפתורים לא מראים שום סימן חיים. נראה שזה התרסק.
הסיבה היא שיישום Delpi הוא הברגה בודדת. הקוד שאתה כותב מייצג רק חבורה של נהלים הנקראים על ידי השרשור הראשי של דלפי בכל פעם שקרה אירוע. בשאר הזמן החוט הראשי הוא טיפול בהודעות מערכת ודברים אחרים כמו פונקציות טיפול בצורות ורכיבים.
לכן, אם לא תסיים את הטיפול באירועים שלך על ידי ביצוע עבודות ממושכות, תמנע מהאפליקציה לטפל בהודעות אלה.
פיתרון נפוץ לסוג כזה של בעיות הוא לקרוא "יישום. ProcessMessages ". "יישום" הוא אובייקט עולמי של מחלקת היישום.
היישום. תהליכי תהליכים מטפלים בכל הודעות ההמתנה כמו תנועות חלונות, לחיצות כפתור וכן הלאה. הוא משמש בדרך כלל כפתרון פשוט כדי להשאיר את היישום שלך "עובד".
למרבה הצער למנגנון שמאחורי "ProcessMessages" יש מאפיינים משלו, העלולים לגרום לבלבול גדול!
מה עושה ProcessMessages?
PprocessMessages מטפל בכל הודעות המערכת הממתינות בתור ההודעות של היישומים. Windows משתמש בהודעות כדי "לדבר" עם כל היישומים הפועלים. אינטראקציה של משתמשים מועברת לטופס באמצעות הודעות ו" ProcessMessages "מטפל בהם.
אם העכבר יורד על כפתור TB, למשל, ProgressMessages עושה את כל מה שצריך לקרות באירוע זה כמו צבוע מחדש את הכפתור למצב "לחוץ" וכמובן, קריאה לנוהל הטיפול ב- OnClick () אם הקצאת כזו.
זו הבעיה: כל קריאה ל- ProcessMessages עשויה להכיל שוב שיחה רקורסיבית לכל מטפל אירועים. להלן דוגמא:
השתמש בקוד הבא עבור מטפל OnClick even של כפתור ("עבודה"). הצהרת החיזיון מדמה עבודת עיבוד ארוכה עם כמה קריאות ל- ProcessMessages מדי פעם.
זה מפשט לטובת הקריאות טובה יותר:
{ב- MyForm:}
רמת עבודה: מספר שלם;
{OnCreate:}
רמת עבודה: = 0;
תהליך TForm1.WorkBtnClick (שולח: TObject);
var
מחזור: מספר שלם;
התחל
inc (WorkLevel);
ל מחזור: = 1 ל 5 לעשות
התחל
Memo1.Lines. הוסף ('- עבודה' + IntToStr (WorkLevel) + ', מחזור' + IntToStr (מחזור);
יישום. ProcessMessages;
שינה (1000); // או יצירה אחרת
סוף;
Memo1.Lines. הוסף ('עבודה' + IntToStr (WorkLevel) + 'הסתיים.');
dec (WorkLevel);
סוף;
ללא "ProcessMessages" השורות הבאות נכתבות לתזכיר, אם הכפתור נלחץ פעמיים תוך זמן קצר:
- עבודה 1, מחזור 1
- עבודה 1, מחזור 2
- עבודה 1, מחזור 3
- עבודה 1, מחזור 4
- עבודה 1, מחזור 5
עבודה 1 הסתיימה.
- עבודה 1, מחזור 1
- עבודה 1, מחזור 2
- עבודה 1, מחזור 3
- עבודה 1, מחזור 4
- עבודה 1, מחזור 5
עבודה 1 הסתיימה.
בעוד ההליך תפוס, הטופס לא מראה שום תגובה, אך הלחיצה השנייה הוכנסה לתור ההודעות על ידי Windows. מיד לאחר סיום ה- "OnClick" הוא ייקרא שוב.
כולל "ProcessMessages", הפלט עשוי להיות שונה מאוד:
- עבודה 1, מחזור 1
- עבודה 1, מחזור 2
- עבודה 1, מחזור 3
- עבודה 2, מחזור 1
- עבודה 2, מחזור 2
- עבודה 2, מחזור 3
- עבודה 2, מחזור 4
- עבודה 2, מחזור 5
עבודה 2 הסתיימה.
- עבודה 1, מחזור 4
- עבודה 1, מחזור 5
עבודה 1 הסתיימה.
הפעם נראה שהטופס עובד שוב ומקבל כל אינטראקציה של משתמשים. אז הכפתור נלחץ לחצי הדרך במהלך פונקציית ה"פועל "הראשונה שלך שוב, שתטופל באופן מיידי. כל האירועים הנכנסים מטופלים כמו כל שיחת פונקציה אחרת.
להלכה, במהלך כל קריאה ל- "ProgressMessages" כל קליקים והודעות משתמש עלולים לקרות "במקום".
אז היזהר בקוד שלך!
דוגמא שונה (בפסאודו-קוד פשוט!):
תהליך OnClickFileWrite ();
var myfile: = TFileStream;
התחל
myfile: = TFileStream.create ('myOutput.txt');
נסה
בזמן BytesReady> 0 לעשות
התחל
המסמך שלי. כתוב (DataBlock);
דצמבר (BytesReady, sizeof (DataBlock));
DataBlock [2]: = # 13; {שורת מבחן 1}
יישום. ProcessMessages;
DataBlock [2]: = # 13; {שורת מבחן 2}
סוף;
סוף סוף
myfile.free;
סוף;
סוף;
פונקציה זו כותבת כמות גדולה של נתונים ומנסה "לבטל את הנעילה" של היישום באמצעות "ProcessMessages" בכל פעם שנכתב בלוק נתונים.
אם המשתמש לוחץ על הכפתור שוב, אותו קוד יבוצע בעוד הקובץ נכתב אליו. כך שלא ניתן לפתוח את הקובץ בפעם השנייה והנוהל נכשל.
אולי היישום שלך יעשה התאוששות שגיאה כמו שחרור המאגרים.
כתוצאה אפשרית "נעילת מסד נתונים" תשוחרר והקוד הראשון "פתאום" יעלה "הפרת גישה" כאשר הוא ניגש אליו. במקרה זה: קו הבדיקה 1 יעבוד, קו הבדיקה 2 יתרסק.
הדרך הטובה יותר:
כדי להקל עליך להגדיר את הטופס כולו "מופעל: = שקר", החוסם את כל קלט המשתמש, אך אינו מראה זאת למשתמש (כל הכפתורים אינם אפורים).
דרך טובה יותר תהיה להגדיר את כל הכפתורים ל"נכים ", אך זה עשוי להיות מורכב אם ברצונך לשמור כפתור אחד" בטל "למשל. כמו כן עליכם לעבור על כל הרכיבים כדי להשבית אותם וכאשר הם מופעלים שוב, עליכם לבדוק אם נותרו חלק במצב המוגבל.
אתה יכול השבת ילד המכולה שולט כאשר המאפיין מופעל משתנה.
כפי שמרמז שם הכיתה "TNotifyEvent", יש להשתמש בו רק לתגובות לטווח הקצר לאירוע. עבור קוד הגוזל זמן הדרך הטובה ביותר היא IMHO להכניס את כל הקוד "האיטי" לחוט משלו.
בנוגע לבעיות עם "PrecessMessages" ו / או הפעלה או השבתה של רכיבים, השימוש ב חוט שני נראה שהוא לא מסובך מדי.
זכור שאפילו קווי קוד פשוטים ומהירים עשויים להיתקע למשך שניות, למשל פתיחת קובץ בכונן דיסק עשויה להמתין עד שיסתיים ההפעלה של הכונן. זה לא נראה טוב במיוחד אם נראה שהאפליקציה שלך קורסת מכיוון שהכונן איטי מדי.
זהו זה. בפעם הבאה שתוסיף "יישום. ProcessMessages ", חשוב פעמיים;)