简体   繁体   English

关闭后,我的 Windows 窗体应用程序仍作为任务运行

[英]My Windows Forms Application is still running as task after closing

I have build a C# Windows Forms Application with Visual Studio 2010.我已经使用 Visual Studio 2010 构建了一个 C# Windows 窗体应用程序。

Run and close application is done successfully within a short time.运行和关闭应用程序在短时间内成功完成。 The process is not running in Task-Manager.该进程未在任务管理器中运行。 As well the debugging process close.调试过程也关闭。 No problem occurs.不会出现问题。

But if the Application is running a while the program does not close and still leave running in Task-Manager (I just open the apllication and do nothing else, just wait few hours to reproduce the problem).但是如果应用程序运行了一段时间,程序不会关闭并且仍然在任务管理器中运行(我只是打开应用程序而不做任何其他事情,只需等待几个小时即可重现问题)。 In Debug mode i have to click on STOP DEBUGGING button to end process.在调试模式下,我必须单击“停止调试”按钮才能结束进程。

What can i do to find the rootcause?我该怎么做才能找到根本原因?

If your project contains more than 1 form than you should go to the events of the last form and double click on "FormClosed" event.如果您的项目包含 1 个以上的表单,您应该转到最后一个表单的事件并双击“FormClosed”事件。 After this action sends you to the code, write between the brackets:在此操作将您发送到代码后,在括号之间写入:

 Application.Exit(); 

A process ends after all of the foreground threads stop.一个进程在所有前台线程停止后结束。

In a typical Winforms application, there's one main foreground thread - the UI thread.在典型的 Winforms 应用程序中,有一个主要的前台线程 - UI 线程。 This stops after the main form (the one used in Application.Run ) is closed.这在主窗体( Application.Run使用的窗体)关闭后停止。 Check what your Main method is doing afterwards, or just put a breakpoint there to see if the thread successfuly之后检查您的Main方法正在做什么,或者只是在那里放置一个断点以查看线程是否成功

You might also have some foreground worker thread if you're doing multi-threading.如果您正在进行多线程处理,您可能还有一些前台工作线程。 It's your responsibility to ensure they're all stopped.您有责任确保它们全部停止。 The tricky part is that some of the classes you're using might spin up such threads on their own, without you knowing about it.棘手的部分是您正在使用的某些类可能会自行启动此类线程,而您对此一无所知。 The first thing to keep in mind is that any object you create that implements IDisposable should actually be disposed.首先要记住的是,您创建的任何实现IDisposable对象实际上都应该被释放。 This might take care of the issue.这可能会解决这个问题。 One example that often causes trouble is System.Threading.Timer (or System.Timers.Timer ) - if you don't Dispose it, it will keep your application running indefinitely.一个经常引起问题的例子是System.Threading.Timer (或System.Timers.Timer )——如果你不Dispose它,它会让你的应用程序无限期地运行。

To investigate the problem, you can use the thread list in Visual Studio's debugger (Debug -> Windows -> Threads).要调查此问题,您可以使用 Visual Studio 调试器中的线程列表(调试 -> Windows -> 线程)。 Run the application, wait as long as needed, close the form and then pause the debugger.运行应用程序,根据需要等待,关闭表单,然后暂停调试器。 The thread list will show all the managed threads in the process.线程列表将显示进程中的所有托管线程。 Look at the location on the threads that are running - double-clicking a thread will switch your debugger view to that thread, and you can then see the call stack.查看正在运行的线程上的位置 - 双击一个线程会将您的调试器视图切换到该线程,然后您可以看到调用堆栈。 This might give you some insight about where that thread came from, and what code it's currently executing (ie why it is stuck).这可能会让您了解该线程的来源以及它当前正在执行的代码(即为什么它被卡住)。 You'll probably see a wait there somewhere (unless it's actually doing CPU work);您可能会在某处看到等待(除非它实际上在执行 CPU 工作); just look at the call stack (Debug -> Windows -> Call Stack) and look for something to identify.只需查看调用堆栈(调试 -> Windows -> 调用堆栈)并查找要识别的内容。

If you checked all the threads, and you can't see anything suspicious in the call stacks, you might have to do a bit of configuration in the debugger.如果检查了所有线程,并且在调用堆栈中看不到任何可疑内容,则可能需要在调试器中进行一些配置。 There's two main things you can try - first, in the call stack window, right click and select "Show external code".您可以尝试两件事 - 首先,在调用堆栈窗口中,右键单击并选择“显示外部代码”。 If that doesn't help, you might have to disable Just My Code (Options -> Debugger), and enable symbol loading for the modules involved.如果这没有帮助,您可能必须禁用仅我的代码(选项 -> 调试器),并为所涉及的模块启用符号加载。 This is a bit more complicated.这有点复杂。

You can use Environment.Exit(0);您可以使用Environment.Exit(0); instead.反而。

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 a thread of your app 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并查看记录的配置文件。 If find my copyTo issue that way.如果找到我的copyTo问题。

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

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

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