簡體   English   中英

關閉后,我的 Windows 窗體應用程序仍作為任務運行

[英]My Windows Forms Application is still running as task after closing

我已經使用 Visual Studio 2010 構建了一個 C# Windows 窗體應用程序。

運行和關閉應用程序在短時間內成功完成。 該進程未在任務管理器中運行。 調試過程也關閉。 不會出現問題。

但是如果應用程序運行了一段時間,程序不會關閉並且仍然在任務管理器中運行(我只是打開應用程序而不做任何其他事情,只需等待幾個小時即可重現問題)。 在調試模式下,我必須單擊“停止調試”按鈕才能結束進程。

我該怎么做才能找到根本原因?

如果您的項目包含 1 個以上的表單,您應該轉到最后一個表單的事件並雙擊“FormClosed”事件。 在此操作將您發送到代碼后,在括號之間寫入:

 Application.Exit(); 

一個進程在所有前台線程停止后結束。

在典型的 Winforms 應用程序中,有一個主要的前台線程 - UI 線程。 這在主窗體( Application.Run使用的窗體)關閉后停止。 之后檢查您的Main方法正在做什么,或者只是在那里放置一個斷點以查看線程是否成功

如果您正在進行多線程處理,您可能還有一些前台工作線程。 您有責任確保它們全部停止。 棘手的部分是您正在使用的某些類可能會自行啟動此類線程,而您對此一無所知。 首先要記住的是,您創建的任何實現IDisposable對象實際上都應該被釋放。 這可能會解決這個問題。 一個經常引起問題的例子是System.Threading.Timer (或System.Timers.Timer )——如果你不Dispose它,它會讓你的應用程序無限期地運行。

要調查此問題,您可以使用 Visual Studio 調試器中的線程列表(調試 -> Windows -> 線程)。 運行應用程序,根據需要等待,關閉表單,然后暫停調試器。 線程列表將顯示進程中的所有托管線程。 查看正在運行的線程上的位置 - 雙擊一個線程會將您的調試器視圖切換到該線程,然后您可以看到調用堆棧。 這可能會讓您了解該線程的來源以及它當前正在執行的代碼(即為什么它被卡住)。 您可能會在某處看到等待(除非它實際上在執行 CPU 工作); 只需查看調用堆棧(調試 -> Windows -> 調用堆棧)並查找要識別的內容。

如果檢查了所有線程,並且在調用堆棧中看不到任何可疑內容,則可能需要在調試器中進行一些配置。 您可以嘗試兩件事 - 首先,在調用堆棧窗口中,右鍵單擊並選擇“顯示外部代碼”。 如果這沒有幫助,您可能必須禁用僅我的代碼(選項 -> 調試器),並為所涉及的模塊啟用符號加載。 這有點復雜。

您可以使用Environment.Exit(0); 反而。

如果該進程仍處於掛起狀態,則意味着您沒有正確處理您的資源。

使用Application.Exit()或要求系統執行Environment.Exit(0)可能會在發生錯誤時記錄在系統中,您最好知道如何正確關閉進程而不是依賴Application.Exit() ,如果你想關閉你的應用程序的一個線程,你必須知道如何收集這些垃圾。

您可以重新實現Dispose方法來處理服務、套接字、流,以及幾乎所有具有.Dispose可用的東西。

 public class MyClass: IMyClass, IDisposable
    {
        private bool _disposed = false;
        // ...
public void Dispose()
{
    Dispose(true);

    GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
    if (_disposed) return;

    if (disposing)
    {
        // dispose your stuff you created in this class
        // do the same for other classes

        // some examples
        /*
        _webClient.Dispose();
        _connector.DataAvailable -= ConnectorHasDataComing
        _socket.Dispose();
        _timer.Dispose();
        _taskLogs.ForEach(x => {
            x.Token.Cancel();
            x.Task.Wait();
            x.Task.Dispose();
        });
        */
    }

    // dispose native events

    _disposed = true;
}

如果您使用System.Threading.ThreadSystem.Threading.Tasks.TaskSystem.IO.MemoryStream (或其他類型的 Stream - Writer/Reader),以及其他需要CancellationTokenSource 如果你創建的類的ressource當你處置類,使用Token.Cancel()方法,讓它知道其父被布置和.Wait()為它調用之前.Dispose()

public async Task Run(CancellationTokenSource cancellationTokenSource)
{
    // ...
    while (Running) {
        if (cancellationTokenSource.IsCancellationRequested) return;
        // ....
    }

    // ....
    using (var reader = new WaveFileReader(tempFile))
    {
         reader.Position = 0;
         await reader.CopyToAsync(fileWriter,81920, cancellationTokenSource.Token);
    }
}

當我的調試在關閉應用程序后仍然掛起時,我使用診斷工具發現了我的問題。

在此處輸入圖片說明

如果您使用 CPU Usage,您可以單擊Break All並設置斷點。 然后您可以查看分析器並找到您的主要功能是什么,您可能會發現您的表單已被釋放,但您有一個線程或任務來調用表單上的字段。

在此處輸入圖片說明

就我而言,我使用的是文件寫入器,並在該類中實現了 IDisposable,但有時它是關於或實際使用.copyTo在文件讀取器與其自身之間傳輸數據,因此它處於掛起狀態而不會引發異常。

在此處輸入圖片說明

單擊一個事件后,單擊Go to Source code並放置一個斷點,您可能會看到您的代碼存儲的事件。

否則,您可以在同一工具中使用選項卡Memory Usage來拍攝快照並查看堆和對象差異或選項卡CPU Usage並查看記錄的配置文件。 如果找到我的copyTo問題。

您還可以使用Throw on all exceptions運行您的應用程序在此處輸入圖片說明

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM