简体   繁体   English

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

[英]Application is still running in memory after Application.Exit() is called

The application I am building is still running in memory (checked in Task Manager) after it is closed using Application.Exit() .我正在构建的应用程序在使用Application.Exit()关闭后仍在内存中运行(在任务管理器中检查Application.Exit() Because of this when I am running it again after closing it as mentioned above, I am getting this error "Only one instance at a time".因此,当我如上所述关闭它后再次运行它时,我收到此错误“一次只有一个实例”。 Can you please tell me how to completely close my application?你能告诉我如何完全关闭我的应用程序吗?

It seems that this is a Windows ap and you are calling System.Windows.Forms.Application.Exit() but there is a thread still running in the background.似乎这是一个 Windows ap,您正在调用 System.Windows.Forms.Application.Exit() 但仍有一个线程仍在后台运行。 Have you tried你有没有尝试过

Application.ExitThread();

Environment.Exit();

You could kill the process as Jonesy mentioned, passing in the process ID of the process if it is a separate application than the current running process.您可以像 Jonesy 提到的那样终止进程,如果它是一个与当前正在运行的进程不同的应用程序,则传入进程的进程 ID。

For that, you need to use the System.Diagnostics.Process namespace and loop through the currently running processes to get the right pid and then call kill on that pid.为此,您需要使用 System.Diagnostics.Process 命名空间并遍历当前正在运行的进程以获取正确的 pid,然后对该 pid 调用 kill。

One time when I had odd behavior (crashing/freezing during Application.Exit() ), I used Process.GetCurrentProcess().CloseMainWindow() .有一次我有奇怪的行为(在Application.Exit()期间崩溃/冻结),我使用了Process.GetCurrentProcess().CloseMainWindow()

That function is in the System.Diagnostics namespace and seems to be better than Kill() since it does not force it to quit the same way.该函数位于System.Diagnostics命名空间中,似乎比Kill()更好,因为它不会强制它以相同的方式退出。

Because of using Foreground Thread and Lybda Expession thread So, threads which will continue to run until the last foreground thread is terminated.因为使用了 Foreground Thread 和 Lybda Expession thread 所以,这些线程会一直运行,直到最后一个前台线程被终止。 In another way, the application is closed when all the foreground threads are stopped.另一种方式是,当所有前台线程都停止时,应用程序就会关闭。 that's why the application won't wait until the background threads are completed, but it will wait until all the foreground threads are terminated.这就是为什么应用程序不会等到后台线程完成,而是会等到所有前台线程都终止。 So In that case we have to explicitly stop the all running threads by using所以在这种情况下,我们必须通过使用显式停止所有正在运行的线程

Environment.Exit(Environment.ExitCode);

This kept the memory managed perfectly, with no memory leak.这样可以完美地管理内存,没有内存泄漏。

If the process is still pending that means you are not disposing your resources properly.如果该进程仍处于挂起状态,则意味着您没有正确处理您的资源。

Using Application.Exit() or asking the system to do it Environment.Exit(0) may be logged in the system as an error occurred and you are better to know how to properly close a process than relied on Application.Exit() , if you want to close just a thread and keep your app running, you have to know how to collect those garbage使用Application.Exit()或要求系统执行Environment.Exit(0)可能会在发生错误时记录在系统中,您最好知道如何正确关闭进程而不是依赖Application.Exit() ,如果你只想关闭一个线程并保持你的应用程序运行,你必须知道如何收集这些垃圾

You can re-implement the Dispose method to Dispose services, sockets, streams, almost everything that has a .Dispose available.您可以重新实现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;
}

If you use System.Threading.Thread or System.Threading.Tasks.Task or System.IO.MemoryStream (or other kind of Stream - Writer/Reader), and others that requires a CancellationTokenSource .如果您使用System.Threading.ThreadSystem.Threading.Tasks.TaskSystem.IO.MemoryStream (或其他类型的 Stream - Writer/Reader),以及其他需要CancellationTokenSource If you created the ressource in the class when you are disposing the class, use the Token.Cancel() method to let it know its parent is being disposed and .Wait() for it before calling .Dispose()如果你创建的类的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);
    }
}

I found my issue using the Diagnostic Tools when my Debug was still pending on something after closing the app.当我的调试在关闭应用程序后仍然挂起时,我使用诊断工具发现了我的问题。

在此处输入图片说明

If you use CPU Usage you can click on Break All and it set a breakpoint.如果您使用 CPU Usage,您可以单击Break All并设置一个断点。 You can then see profiler and find what are your top functions, you might find out that your form is disposed but you have a Thread or Task that invokes fields on your form.然后您可以查看分析器并找到您的主要功能是什么,您可能会发现您的表单已被释放,但您有一个线程或任务来调用表单上的字段。

在此处输入图片说明

For my case, I was using a filewriter and I implemented IDisposable in that class but it sometimes was about or actual doing a transfer of data between a filereader and itself using .copyTo so it was pending without throwing an exception.就我而言,我使用的是文件写入器,并在该类中实现了 IDisposable,但有时它是关于或实际使用.copyTo在文件读取器与其自身之间传输数据,因此它处于挂起状态而不会引发异常。

在此处输入图片说明

After clicking on one the events, click on Go to Source code and place a breakpoint, you may see events that your code is stocked on.单击一个事件后,单击Go to Source code并放置一个断点,您可能会看到您的代码存储的事件。

Otherwise, you can use in the same tool the tab Memory Usage to take a snapshot and look at the Heap and Objects diff or the tab CPU Usage and look at a recorded Profile.否则,您可以在同一工具中使用选项卡Memory Usage来拍摄快照并查看堆和对象差异或选项卡CPU Usage并查看记录的配置文件。 I found my copyTo issue that way.我以这种方式找到了我的copyTo问题。

You can also run your app with Throw on all exceptions您还可以使用Throw on all exceptions运行您的应用程序在此处输入图片说明

while disposing make sure no one recalls the form or its instance.在处理时确保没有人回忆起表单或其实例。

Also, if you are using the form event _FormClosing此外,如果您正在使用表单事件_FormClosing

Make sure if you have a modal to cancel the form closing, return and set e.Cancel = true;确保您有一个模式来取消关闭表单,返回并设置e.Cancel = true; but do not set e.Cancel = true if the form is closing.但如果表单正在关闭,请不要设置e.Cancel = true And do not call this.Close() in a _FormClosing() event that you are handling yourself.并且不要在您自己处理的_FormClosing()事件中调用this.Close()

After, you may .Dispose() your stuff, but make sure no Dispose methods call the form back like invoking components, since they are being disposed or already disposed.之后,你可以.Dispose()你的东西,但确保没有 Dispose 方法像调用组件一样调用表单,因为它们正在被释放或已经被.Dispose()

For people that use the hack that sets the form in an var instance to have access to it anywhere, do not dispose it, otherwise you are disposing a form already disposed.对于使用 hack 在var instance中设置表单以在任何地方访问它的人,不要处置它,否则你正在处置一个已经处置的表单。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM