לדלג לתוכן

פתרון סגירה שממתינה לעבודה

נשמור שדה Task של הפעולה הנוכחית, דגל closingRequested ודגל allowClose. בתחילת Closing, אם allowClose אמת נאפשר יציאה. אחרת נסמן את האירוע כמבוטל, נבטל את token ונפעיל פעם אחת מסלול אסינכרוני שממתין למשימה. ב־finally שלו נסמן allowClose ונקרא Close על ה־dispatcher. ההמתנה חייבת להיות await, ולא Wait שחוסם את ההמשך של הפעולה עצמה.

קטע הקוד הבא מציג את גבול הסגירה בתוך מחלקת Window שהורחבה עם השדות המתאימים. המשימה running מייצגת את כל cleanup של הפעולה, ולא רק את החישוב.

private bool closingRequested;
private bool allowClose;
private Task running = Task.CompletedTask;

private async void OnClosing(object? sender, CancelEventArgs args)
{
    if (allowClose) return;
    args.Cancel = true;
    if (closingRequested) return;
    closingRequested = true;
    stop?.Cancel();
    try { await running; }
    catch (Exception error) { state.Text = error.Message; }
    finally { allowClose = true; Close(); }
}

ה־handler של ההפעלה יקרא לפונקציה Task-returning וישמור את המשימה לפני שימתין לה. אם תשאירו את החישוב רק בתוך async void, לא יהיה ל־Closing חוזה שאפשר להצטרף אליו. ה־progress יכול להשתמש ב־Stopwatch ולהחליט ב־worker אם עברו לפחות 100 מילישניות לפני פרסום עדכון; הפרסום עצמו יחזור להקשר המתאים.

בממשק COM שאינו agile נצטרך לקיים את חוזה ה־apartment וה־marshalling שלו. תהליכון worker מקרי מן ה־ThreadPool אינו מספק אוטומטית STA או message loop. במקרה של thread ייעודי נבנה גם פרוטוקול shutdown שמסיים בקשות ומשחרר את האובייקטים בהקשר הנדרש, ולא רק נקרא Thread.Join בסוף.