![](/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.