זהו פרויקט הבדיקה הבא שלי כדי לראות איזו ספריית השחלה עבור דלפי תתאים לי הכי טוב למשימת "סריקת הקבצים" שהייתי רוצה לעבד במספר שרשורים / בבריכת פתילים.
כדי לחזור על המטרה שלי: הפוך את "סריקת הקבצים" ברצף של 500-2000 + קבצים מהגישה הלא הברגה לגישה מושחלת. אסור לי 500 חוטים לרוץ בפעם אחת, לכן ברצוני להשתמש בבריכת חוטים. בריכת חוטים היא מעמד דמוי תור שמאכיל מספר חוטים רצים עם המשימה הבאה מהתור.
הניסיון הראשון (הבסיסי ביותר) נעשה על ידי פשוט הרחבת מחלקת ה- TThread והטמעת שיטת ה- Execute (ניתוח המיתרים המשורשר שלי).
מכיוון שלדלפי אין מחלקת בריכת חוטים מיושמת מחוץ לקופסה, בניסיוני השני ניסיתי להשתמש ב- OmniThreadLibrary של פרימוז גבריאלצ'יץ '.
OTL הוא פנטסטי, יש דרכים זיליון להריץ משימה ברקע, דרך ללכת אם אתה רוצה גישה "אש ושכח" למסירת ביצוע מושחל של קטעי קוד שלך.
AsyncCalls מאת אנדראס האוסלאדן
הערה: להלן יהיה קל יותר לעקוב אחרי שתוריד לראשונה את קוד המקור.
תוך כדי בדיקת דרכים נוספות לבצע כמה מהפונקציות שלי מבוצעות בצורה משורשרת, החלטתי לנסות גם את יחידת "AsyncCalls.pas" שפותחה על ידי אנדראס האוסלאדן. אנדי
AsyncCalls - שיחות פונקציה אסינכרוניות היחידה היא ספרייה נוספת שמפתחת דלפי יכולה להשתמש בה כדי להקל על הכאב של יישום גישה משורשרת לביצוע קוד כלשהו.מהבלוג של אנדי: בעזרת AsyncCalls תוכלו לבצע פונקציות מרובות בו זמנית ולסנכרן אותן בכל נקודה בפונקציה או בשיטה שהפעילה אותן…. יחידת AsyncCalls מציעה מגוון אבות-טיפוס של פונקציות לקריאה של פונקציות אסינכרוניות. זה מיישם בריכת חוטים! ההתקנה סופר קלה: פשוט השתמש באסינכרונים מכל אחת מהיחידות שלך ויש לך גישה מיידית לדברים כמו "לבצע בחוט נפרד, סנכרן את ממשק המשתמש הראשי, חכה עד שיסיים".
לצד AsyncCalls ללא רישיון לשימוש (רישיון MPL), אנדי מפרסם לעתים קרובות גם תיקונים משלו עבור ה- Delphi IDE כמו "דלפי להאיץ"ו-"DDevExtensions"אני בטוח ששמעת עליו (אם כבר לא השתמשת).
AsyncCalls בפעולה
בעיקרו של דבר, כל פונקציות AsyncCall מחזירות ממשק IAsyncCall המאפשר לסנכרן את הפונקציות. IAsnycCall חושף את השיטות הבאות:
//v 2.98 של asynccalls.pas
IAsyncCall = ממשק
// ממתין עד לסיום הפונקציה ומחזיר את ערך ההחזרה
פונקציה סנכרון: מספר שלם;
// מחזירה נכון לאחר סיום פונקציית האסינכרון
פונקציה סיים: בוליאני;
// מחזירה את ערך ההחזרה של פונקציית האסינכרון, כאשר סיום הוא TRUE
פונקציה ReturnValue: מספר שלם;
// אומר ל- AsyncCalls כי אסור לבצע את הפונקציה שהוקצתה בשרשור הנוכחי
נוהל ForceDifferentThread;
סוף;
להלן דוגמה לקריאה לשיטה המצפה לשני פרמטרים שלמים שלמים (החזרת IAsyncCall):
TAsyncCalls. להפעיל (AsyncMethod, i, אקראי (500));
פונקציה TAsyncCallsForm. AsyncMethod (taskNr, sleepTime: integer): מספר שלם;
התחל
תוצאה: = sleepTime;
שינה (זמן שינה);
TAsyncCalls. VCLInvoke (
תהליך
התחל
יומן (פורמט ('נעשה> מס':% d / משימות:% d / ישן:% d ', [tasknr, asyncHelper. TaskCount, sleepTime]));
סוף);
סוף;
מתקני ה- TAsyncCalls. VCLInvoke היא דרך לבצע סנכרון עם החוט הראשי שלך (השרשור הראשי של היישום - ממשק המשתמש של היישום שלך). VCLInvoke חוזר מייד. השיטה האנונימית תבוצע בשרשור הראשי. יש גם VCLSync שחוזר כאשר נקראה השיטה האנונימית בשרשור הראשי.
בריכת חוט ב- AsyncCalls
בחזרה למשימת "סריקת הקבצים" שלי: כאשר מזינים (בלולאה למעקב) את מאגר החוטים של אסינקלס עם סדרת TAsyncCalls. קוראים שיחות (), המשימות יתווספו פנימה לבריכה ויבוצעו "בבוא הזמן" (כאשר השיחות שהוספו בעבר הסתיימו).
המתן לכל שיחות ה- IA Sync לסיים
הפונקציה AsyncMultiSync המוגדרת ב- asnyccalls ממתינה לביצוע שיחות ה- async (וידיות אחרות). יש כמה עמוס יתר להלן דרכים להתקשר ל- AsyncMultiSync, והנה הדרך הפשוטה ביותר היא:
פונקציה AsyncMultiSync (const רשימה: מגוון של IAsyncCall; WaitAll: בוליאני = נכון; אלפיות השנייה: קרדינל = INFINITE): קרדינל;
אם אני רוצה ליישם את "לחכות הכל", אני צריך למלא מערך של IAsyncCall ולעשות AsyncMultiSync בפרוסות של 61.
עוזר AsnycCalls שלי
להלן קטע מ- Help SyncCalls Help:
אזהרה: קוד חלקי! (הקוד המלא זמין להורדה)
שימושים AsyncCalls;
סוג
TIAsyncCallArray = מגוון של IAsyncCall;
TIAsyncCallArrays = מגוון של TIAsyncCallArray;
TAsyncCallsHelper = מעמד
פרטי
fTasks: TIAsyncCallArrays;
תכונה משימות: TIAsyncCallArrays לקרוא fTasks;
ציבורי
תהליך AddTask (const שיחה: IAsyncCall);
תהליך המתן הכל;
סוף;
אזהרה: קוד חלקי!
תהליך TAsyncCalls עוזר. המתן הכל;
var
i: מספר שלם;
התחל
ל i: = גבוה (משימות) עד ל נמוך (משימות) לעשות
התחל
AsyncCalls. AsyncMultiSync (משימות [i]);
סוף;
סוף;
בדרך זו אני יכול "לחכות הכל" בגושים של 61 (MAXIMUM_ASYNC_WAIT_OBJECTS) - כלומר לחכות למערכים של IAsyncCall.
עם האמור לעיל, הקוד העיקרי שלי להזנת בריכת החוטים נראה:
תהליך TAsyncCallsForm.btnAddTasks לחץ (שולח: TObject);
const
nrItems = 200;
var
i: מספר שלם;
התחל
AsyncHelper. MaxThreads: = 2 * מערכת. CPUCount;
ClearLog ('מתחיל');
ל i: = 1 עד מספר פריטים לעשות
התחל
AsyncHelper. AddTask (TAsyncCalls. להפעיל (AsyncMethod, i, אקראי (500)));
סוף;
יומן ('הכל פנימה');
// המתן הכל
//asyncHelper.WaitAll;
// או להתיר את ביטול הכל שלא התחיל על ידי לחיצה על כפתור "בטל הכל":
בזמן שלא AsyncHelper. הכל נגמר לעשות יישום. ProcessMessages;
יומן ('סיים');
סוף;
לבטל הכל? - צריך לשנות את AsyncCalls.pas :(
אני גם רוצה שתהיה דרך "לבטל" את המשימות הנמצאות בבריכה אך מחכות לביצוען.
לרוע המזל, AsyncCalls.pas אינו מספק דרך פשוטה לבטל משימה לאחר שנוספה לבריכת החוטים. אין IA SyncCall. בטל או IA SyncCall. DontDoIfNotAlready ביצוע או IA SyncCall. אל תתייחס אליי.
כדי שזה יעבוד הייתי צריך לשנות את AsyncCalls.pas על ידי ניסיון לשנות את זה כמה שפחות - כך שכשאנדי משחרר גרסה חדשה, אני רק צריך להוסיף כמה שורות כדי לקבל את הרעיון "ביטול המשימה" שלי עובד.
הנה מה שעשיתי: הוספתי "הליך ביטול" ל- IA SyncCall. נוהל הביטול מגדיר את שדה "FCancelled" (נוסף) אשר נבדק כאשר הבריכה עומדת להתחיל לבצע את המשימה. הייתי צריך לשנות מעט את IAsyncCall. סיימו (כך שדיווח על שיחה הסתיים גם בביטול) ו- TAsyncCall. נוהל InternExecuteAsyncCall (לא לבצע את השיחה אם היא בוטלה).
אתה יכול להשתמש WinMerge כדי לאתר בקלות הבדלים בין asynccall.pas המקורי של אנדי לבין הגרסה המשונה שלי (כלול בהורדה).
אתה יכול להוריד את קוד המקור המלא ולחקור.
וידוי
הודעה! :)
ה ביטול הרשמה שיטה מונעת את הפתיחה של AsyncCall. אם AsyncCall כבר מעובד, קריאה לביטול חשיפה אינה משפיעה והפונקציה שבוטלה תחזיר שווא שכן AsyncCall לא בוטל.
ה מבוטל השיטה מחזירה נכון אם AsyncCall בוטל על ידי CancelInvocation.
ה לשכוח השיטה מבטלת את הקישור לממשק IAsyncCall ממערכת AsyncCall הפנימית. המשמעות היא שאם לא נעלמה ההתייחסות האחרונה לממשק IAsyncCall, השיחה האסינכרונית עדיין תבוצע. שיטות הממשק יזרקו חריגה אם יתקשרו לאחר שתתקשרו ל- Forget. אסור לפונקציה async להתקשר לחוט הראשי מכיוון שניתן לבצע אותה לאחר ה- TThread. מנגנון הסנכרון / תור הושבת על ידי ה- RTL מה שעלול לגרום למנעול מת.
עם זאת, שים לב שאתה עדיין יכול להפיק תועלת מה- AsyncCallsHelper שלי אם אתה צריך לחכות לכל שיחות אסינכרון שיסיימו עם "asyncHelper. WaitAll "; או אם אתה צריך "לבטל את כל".