![](/img/trans.png)
[英]Closing MDIParent Form, application is still running in task manager
[英]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.Thread
或System.Threading.Tasks.Task
或System.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
問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.