繁体   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