调用 Application.Exit() 后,应用程序仍在内存中运行

Posted

技术标签:

【中文标题】调用 Application.Exit() 后,应用程序仍在内存中运行【英文标题】:Application is still running in memory after Application.Exit() is called 【发布时间】:2014-07-31 03:24:25 【问题描述】:

我正在构建的应用程序在使用Application.Exit() 关闭后仍在内存中运行(在任务管理器中检查)。因此,当我在如上所述关闭它后再次运行它时,我收到此错误“一次只有一个实例”。你能告诉我如何完全关闭我的应用程序吗?

【问题讨论】:

您使用的是Microsoft.Office.Interop吗? (例如 Excel) 您是否尝试在它退出后附加到它,暂停线程并查看仍然处于活动状态的内容?不确定应用程序退出语义,但前台线程通常不想退出,除非明确结束 Process.GetCurrentProcess().Kill();怎么样 谢谢琼斯,这很有效。 【参考方案1】:

这似乎是一个 Windows 应用程序,您正在调用 System.Windows.Forms.Application.Exit() 但有一个线程仍在后台运行。你试过了吗

Application.ExitThread();

Environment.Exit();

您可以像 Jonesy 提到的那样终止该进程,如果它是与当前正在运行的进程不同的应用程序,则传入该进程的进程 ID。

为此,您需要使用 System.Diagnostics.Process 命名空间并遍历当前正在运行的进程以获取正确的 pid,然后对该 pid 调用 kill。

【讨论】:

谢谢罗伯特,帮了大忙。【参考方案2】:

有一次我有奇怪的行为(在Application.Exit() 期间崩溃/冻结),我使用了Process.GetCurrentProcess().CloseMainWindow()

该函数位于 System.Diagnostics 命名空间中,并且似乎比 Kill() 更好,因为它不会强制它以相同的方式退出。

【讨论】:

【参考方案3】:

由于使用了Foreground Thread和Lybda Expession thread,所以,线程会继续运行,直到最后一个前台线程终止。换句话说,当所有前台线程都停止时,应用程序就会关闭。 这就是为什么应用程序不会等到后台线程完成,而是会等到所有前台线程都终止。 所以在这种情况下,我们必须使用

显式停止所有正在运行的线程

Environment.Exit(Environment.ExitCode);

这样可以完美地管理内存,没有内存泄漏。

【讨论】:

这不会为 3 岁接受的答案添加任何内容。 这个答案帮助我处理了接受的答案中不存在的返回码(Environment.ExitCode),如果输入会导致错误,它会在 2020 年显示。【参考方案4】:

如果该过程仍处于待处理状态,则意味着您没有正确处理资源。

使用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(或其他类型的流 - 写入器/读取器),以及其他需要CancellationTokenSource 的东西。如果您在处置类时在类中创建了资源,请在调用.Dispose()之前使用Token.Cancel() 方法让它知道它的父级正在被处置并为它提供.Wait()

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 使用率,您可以单击 Break All 并设置断点。 然后您可以查看分析器并找到最重要的功能,您可能会发现您的表单已被释放,但您有一个调用表单上的字段的线程或任务。

就我而言,我使用了一个文件写入器,并在该类中实现了 IDisposable,但它有时是关于或实际使用 .copyTo 在文件读取器与其自身之间进行数据传输,因此它处于挂起状态而不会引发异常。

点击一个事件后,点击Go to Source code并放置一个断点,你可能会看到你的代码存放的事件。

否则,您可以在同一工具中使用标签Memory Usage 拍摄快照并查看堆和对象差异或标签CPU Usage 并查看记录的配置文件。我通过这种方式找到了我的copyTo 问题。

您也可以使用Throw on all exceptions 运行您的应用程序

在处理时确保没有人记得表单或其实例。

另外,如果你使用表单事件_FormClosing

确保您有一个模式来取消表单关闭,返回并设置e.Cancel = true;,但如果表单正在关闭,请不要设置e.Cancel = true。并且不要在您自己处理的_FormClosing() 事件中调用this.Close()

之后,您可以.Dispose() 您的东西,但请确保没有 Dispose 方法像调用组件那样回调表单,因为它们正在被释放或已经被释放。

对于那些使用在var instance 中设置表单以在任何地方访问它的黑客,不要处置它,否则您正在处置一个已经处置的表单。

【讨论】:

以上是关于调用 Application.Exit() 后,应用程序仍在内存中运行的主要内容,如果未能解决你的问题,请参考以下文章

Application.Exit() - 关闭表单后应用程序仍在运行

Application.Exit()结束程序,但线程还在的解决方法。

在winForm中

Winforms:Application.Exit vs Environment.Exit vs Form.Close

c#中退出WinForm程序包括有很多方法,如:this.Close(); Application.Exit();Application.ExitThread(); System.Environmen

c#多线程