如何确定谁拥有当我的应用程序退出时仍在运行的工作线程?

发布于 2024-10-08 20:30:44 字数 4916 浏览 0 评论 0原文

升级到 VS2010 后不久,我的应用程序无法完全关闭。如果我关闭应用程序,然后在 IDE 中点击暂停,我会看到以下内容:

alt text

问题是,没有上下文。调用堆栈只显示[外部代码],这并没有太大帮助。

到目前为止,我已经做了一些尝试缩小问题范围的操作:

  • 删除所有无关的插件,以最大程度地减少启动的工作线程的数量
  • ,在我创建工作线程(以及委托+ BeginInvoke,因为我认为它们被标记)的任何地方的代码中设置断点无论如何,调试器中的“工作线程”)。没有人被击中。
  • 对所有线程设置 IsBackground = true

虽然我可以执行下一个强力步骤,即将我的代码回滚到不会发生这种情况的点,然后查看所有更改日志,这不是非常有效。鉴于调试器明显缺乏信息,任何人都可以推荐一种更好的方法来解决这个问题吗?

我能想到的唯一其他事情包括:

  • 阅读 WinDbg 并尝试使用它在线程启动时停止。至少,我认为这是可能的......:)
  • 注释掉大块代码,直到应用程序正确关闭,然后开始取消注释,直到它不正确关闭为止。

更新

也许这些信息会有用。我决定使用 WinDbg 并附加到我的应用程序。然后我关闭它,切换到线程 0 并转储堆栈内容。这就是我所拥有的:

ThreadCount:      6
UnstartedThread:  0
BackgroundThread: 1
PendingThread:    0
DeadThread:       4
Hosted Runtime:   no
                                   PreEmptive   GC Alloc                Lock
       ID  OSID ThreadOBJ    State GC           Context       Domain   Count APT Exception
   0    1  1c70 005a65c8      6020 Enabled  02dac6e0:02dad7f8 005a03c0     0 STA
   2    2  1b20 005b1980      b220 Enabled  00000000:00000000 005a03c0     0 MTA (Finalizer)
XXXX    3       08504048     19820 Enabled  00000000:00000000 005a03c0     0 Ukn
XXXX    4       08504540     19820 Enabled  00000000:00000000 005a03c0     0 Ukn
XXXX    5       08516a90     19820 Enabled  00000000:00000000 005a03c0     0 Ukn
XXXX    6       08517260     19820 Enabled  00000000:00000000 005a03c0     0 Ukn
0:008> ~0s
eax=c0674960 ebx=00000000 ecx=00000000 edx=00000000 esi=0040f320 edi=005a65c8
eip=76c37e47 esp=0040f23c ebp=0040f258 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
USER32!NtUserGetMessage+0x15:
76c37e47 83c404          add     esp,4
0:000> !clrstack
OS Thread Id: 0x1c70 (0)
Child SP IP       Call Site
0040f274 76c37e47 [InlinedCallFrame: 0040f274] 
0040f270 6baa8976 DomainBoundILStubClass.IL_STUB_PInvoke(System.Windows.Interop.MSG ByRef, System.Runtime.InteropServices.HandleRef, Int32, Int32)*** WARNING: Unable to verify checksum for C:\Windows\assembly\NativeImages_v4.0.30319_32\WindowsBase\d17606e813f01376bd0def23726ecc62\WindowsBase.ni.dll

0040f274 6ba924c5 [InlinedCallFrame: 0040f274] MS.Win32.UnsafeNativeMethods.IntGetMessageW(System.Windows.Interop.MSG ByRef, System.Runtime.InteropServices.HandleRef, Int32, Int32)
0040f2c4 6ba924c5 MS.Win32.UnsafeNativeMethods.GetMessageW(System.Windows.Interop.MSG ByRef, System.Runtime.InteropServices.HandleRef, Int32, Int32)
0040f2dc 6ba8e5f8 System.Windows.Threading.Dispatcher.GetMessage(System.Windows.Interop.MSG ByRef, IntPtr, Int32, Int32)
0040f318 6ba8d579 System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame)
0040f368 6ba8d2a1 System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame)
0040f374 6ba7fba0 System.Windows.Threading.Dispatcher.Run()
0040f380 62e6ccbb System.Windows.Application.RunDispatcher(System.Object)*** WARNING: Unable to verify checksum for C:\Windows\assembly\NativeImages_v4.0.30319_32\PresentationFramewo#\7f91eecda3ff7ce478146b6458580c98\PresentationFramework.ni.dll

0040f38c 62e6c8ff System.Windows.Application.RunInternal(System.Windows.Window)
0040f3b0 62e6c682 System.Windows.Application.Run(System.Windows.Window)
0040f3c0 62e6c30b System.Windows.Application.Run()
0040f3cc 001f00bc MyApplication.App.Main() [C:\code\trunk\MyApplication\obj\Debug\GeneratedInternalTypeHelper.g.cs @ 24]
0040f608 66c421db [GCFrame: 0040f608]

编辑 - 不确定这是否有帮助,但主线程的调用堆栈如下所示:

    [Managed to Native Transition]  
>   WindowsBase.dll!MS.Win32.UnsafeNativeMethods.GetMessageW(ref System.Windows.Interop.MSG msg, System.Runtime.InteropServices.HandleRef hWnd, int uMsgFilterMin, int uMsgFilterMax) + 0x15 bytes  
    WindowsBase.dll!System.Windows.Threading.Dispatcher.GetMessage(ref System.Windows.Interop.MSG msg, System.IntPtr hwnd, int minMessage, int maxMessage) + 0x48 bytes 
    WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame frame = {System.Windows.Threading.DispatcherFrame}) + 0x85 bytes 
    WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame frame) + 0x49 bytes  
    WindowsBase.dll!System.Windows.Threading.Dispatcher.Run() + 0x4c bytes  
    PresentationFramework.dll!System.Windows.Application.RunDispatcher(object ignore) + 0x17 bytes  
    PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window window) + 0x6f bytes 
    PresentationFramework.dll!System.Windows.Application.Run(System.Windows.Window window) + 0x26 bytes 
    PresentationFramework.dll!System.Windows.Application.Run() + 0x1b bytes 

我对其进行了搜索,发现了一些与 WPF GUI 挂起相关的帖子,也许这会给我更多线索。

Not long after upgrading to VS2010, my application won't shut down cleanly. If I close the app and then hit pause in the IDE, I see this:

alt text

The problem is, there's no context. The call stack just says [External code], which isn't too helpful.

Here's what I've done so far to try to narrow down the problem:

  • deleted all extraneous plugins to minimize the number of worker threads launched
  • set breakpoints in my code anywhere I create worker threads (and delegates + BeginInvoke, since I think they are labeled "Worker Thread" in the debugger anyway). None were hit.
  • set IsBackground = true for all threads

While I could do the next brute force step, which is to roll my code back to a point where this didn't happen and then look over all of the change logs, this isn't terribly efficient. Can anyone recommend a better way to figure this out, given the notable lack of information presented by the debugger?

The only other things I can think of include:

  • read up on WinDbg and try to use it to stop anytime a thread is started. At least, I thought that was possible... :)
  • comment out huge blocks of code until the app closes properly, then start uncommenting until it doesn't.

UPDATE

Perhaps this information will be of use. I decided to use WinDbg and attach to my application. I then closed it, and switched to thread 0 and dumped the stack contents. Here's what I have:

ThreadCount:      6
UnstartedThread:  0
BackgroundThread: 1
PendingThread:    0
DeadThread:       4
Hosted Runtime:   no
                                   PreEmptive   GC Alloc                Lock
       ID  OSID ThreadOBJ    State GC           Context       Domain   Count APT Exception
   0    1  1c70 005a65c8      6020 Enabled  02dac6e0:02dad7f8 005a03c0     0 STA
   2    2  1b20 005b1980      b220 Enabled  00000000:00000000 005a03c0     0 MTA (Finalizer)
XXXX    3       08504048     19820 Enabled  00000000:00000000 005a03c0     0 Ukn
XXXX    4       08504540     19820 Enabled  00000000:00000000 005a03c0     0 Ukn
XXXX    5       08516a90     19820 Enabled  00000000:00000000 005a03c0     0 Ukn
XXXX    6       08517260     19820 Enabled  00000000:00000000 005a03c0     0 Ukn
0:008> ~0s
eax=c0674960 ebx=00000000 ecx=00000000 edx=00000000 esi=0040f320 edi=005a65c8
eip=76c37e47 esp=0040f23c ebp=0040f258 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
USER32!NtUserGetMessage+0x15:
76c37e47 83c404          add     esp,4
0:000> !clrstack
OS Thread Id: 0x1c70 (0)
Child SP IP       Call Site
0040f274 76c37e47 [InlinedCallFrame: 0040f274] 
0040f270 6baa8976 DomainBoundILStubClass.IL_STUB_PInvoke(System.Windows.Interop.MSG ByRef, System.Runtime.InteropServices.HandleRef, Int32, Int32)*** WARNING: Unable to verify checksum for C:\Windows\assembly\NativeImages_v4.0.30319_32\WindowsBase\d17606e813f01376bd0def23726ecc62\WindowsBase.ni.dll

0040f274 6ba924c5 [InlinedCallFrame: 0040f274] MS.Win32.UnsafeNativeMethods.IntGetMessageW(System.Windows.Interop.MSG ByRef, System.Runtime.InteropServices.HandleRef, Int32, Int32)
0040f2c4 6ba924c5 MS.Win32.UnsafeNativeMethods.GetMessageW(System.Windows.Interop.MSG ByRef, System.Runtime.InteropServices.HandleRef, Int32, Int32)
0040f2dc 6ba8e5f8 System.Windows.Threading.Dispatcher.GetMessage(System.Windows.Interop.MSG ByRef, IntPtr, Int32, Int32)
0040f318 6ba8d579 System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame)
0040f368 6ba8d2a1 System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame)
0040f374 6ba7fba0 System.Windows.Threading.Dispatcher.Run()
0040f380 62e6ccbb System.Windows.Application.RunDispatcher(System.Object)*** WARNING: Unable to verify checksum for C:\Windows\assembly\NativeImages_v4.0.30319_32\PresentationFramewo#\7f91eecda3ff7ce478146b6458580c98\PresentationFramework.ni.dll

0040f38c 62e6c8ff System.Windows.Application.RunInternal(System.Windows.Window)
0040f3b0 62e6c682 System.Windows.Application.Run(System.Windows.Window)
0040f3c0 62e6c30b System.Windows.Application.Run()
0040f3cc 001f00bc MyApplication.App.Main() [C:\code\trunk\MyApplication\obj\Debug\GeneratedInternalTypeHelper.g.cs @ 24]
0040f608 66c421db [GCFrame: 0040f608]

EDIT -- not sure if this helps, but the main thread's call stack looks like this:

    [Managed to Native Transition]  
>   WindowsBase.dll!MS.Win32.UnsafeNativeMethods.GetMessageW(ref System.Windows.Interop.MSG msg, System.Runtime.InteropServices.HandleRef hWnd, int uMsgFilterMin, int uMsgFilterMax) + 0x15 bytes  
    WindowsBase.dll!System.Windows.Threading.Dispatcher.GetMessage(ref System.Windows.Interop.MSG msg, System.IntPtr hwnd, int minMessage, int maxMessage) + 0x48 bytes 
    WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame frame = {System.Windows.Threading.DispatcherFrame}) + 0x85 bytes 
    WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame frame) + 0x49 bytes  
    WindowsBase.dll!System.Windows.Threading.Dispatcher.Run() + 0x4c bytes  
    PresentationFramework.dll!System.Windows.Application.RunDispatcher(object ignore) + 0x17 bytes  
    PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window window) + 0x6f bytes 
    PresentationFramework.dll!System.Windows.Application.Run(System.Windows.Window window) + 0x26 bytes 
    PresentationFramework.dll!System.Windows.Application.Run() + 0x1b bytes 

I did a search on it and found some posts related to WPF GUIs hanging, and maybe that'll give me some more clues.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

背叛残局 2024-10-15 20:30:44

将以下处理程序添加到应用程序在单独线程中创建的每个窗口:

win.Closed += (o, e) => win.Dispatcher.InvokeShutdown();

如果主线程挂起,请在 MainWindow.Closed 中调用 win.Dispatcher.InvokeShutdown() - 这将自动关闭在主线程中创建的所有其他窗口。

如果没有这个处理程序,我的应用程序(具有以下代码)也会在退出时挂起:

void Worker() {
    var win = new Window();
    // win.Closed += onWindowClose ?? ((o, e) => editor.Dispatcher.InvokeShutdown());
    editor.Show();
    System.Windows.Threading.Dispatcher.Run();
}

Add the following handler to every window your application creates in a separate thread:

win.Closed += (o, e) => win.Dispatcher.InvokeShutdown();

If the main thread hangs, call win.Dispatcher.InvokeShutdown() in MainWindow.Closed - this will automatically close all other windows created in the Main thread.

Without this handler, my application with the following code hanged on exit too:

void Worker() {
    var win = new Window();
    // win.Closed += onWindowClose ?? ((o, e) => editor.Dispatcher.InvokeShutdown());
    editor.Show();
    System.Windows.Threading.Dispatcher.Run();
}
独木成林 2024-10-15 20:30:44

我终于想通了这个问题。我有一个由 MEF 导入的控件,但实际上尚未调用(尚未)。我认为 MEF 实例化了它,即使它没有在任何地方被引用(我假设创建没有发生直到请求资源,但显然我错了)。我通过使用 Lazy<> 解决了这个问题实例化,现在它可以工作了。这真的让我很困惑,但感谢大家的帮助。我在尝试调试这个问题时学到了很多东西。

I finally figured out this problem. I had a control that was Imported by MEF, but not actually ever called (yet). I think MEF instantiated it even though it wasn't referenced anywhere (I was under the assumption that creation didn't occur until the resource was requested, but apparently I was wrong). I fixed the issue by using Lazy<> instantiation, and now it works. This one really threw me, but thanks everyone for the help. I learned a lot trying to debug this problem.

抚你发端 2024-10-15 20:30:44

您看到的工作线程 ID 是 0。这是一个框架线程并且是预期的 - 它不是程序“生成”的线程。如果您附加到任何 .Net 进程,您都会看到这一点。我不确定它是哪个框架线程 - 绝对不是终结器线程,因为它永远不是线程 0。也许是 JIT 线程?

更有趣的是你的主线程,因为它似乎是挂着的。我会集中精力调试你的主线程以解决这个问题。例如,它是否因窗口关闭事件处理程序而陷入僵局,等待永远不会发生的事情?

更新

在阅读了添加到问题中的主线程的堆栈跟踪后,运行一个测试来了解主线程是否已停止或是否只是空闲等待消息(并且它正在等待从未收到或从未发送过的 WM_CLOSE)。

下面的代码可用于手动向您的应用程序发送 WM_CLOSE 消息。只需等待程序在关闭后挂起,然后运行代码即可。将进程名称替换为您自己的进程名称。

更新 2

好吧,看起来主线程确实挂起了,因为它没有处理 WM_CLOSE 或 WM_QUIT 消息。

请尝试制作可以重现问题的最小应用程序并发布代码。

WM_CLOSE\WM_QUIT 应用程序示例

internal class Program
{
    private const int WM_QUIT = 0x0012;
    private const int WM_CLOSE = 0x0010;

    [DllImport("user32.dll")]
    private static extern bool PostMessage(int hhwnd, uint msg, IntPtr wParam, IntPtr lParam);

    private static void Main()
    {
        Process p = GetProcess("Your process name - no '.exe' required");

        CloseMainWindow(p);
    }

    private static Process GetProcess(string name)
    {
        List<Process> processes = Process.GetProcessesByName(name).ToList();

        if (processes.Count != 1)
        {
            throw new Exception(
              "Expected 1 process with name '" + name +
              "' but found " + processes.Count + ".");
        }

        return processes[0];
    }

    private static void CloseMainWindow(Process p)
    {
        PostMessage(p, WM_CLOSE, "close");
    }

    private static void QuitApplication(Process p)
    {
        PostMessage(p, WM_QUIT, "quit");
    }

    private static void PostMessage(Process p, uint message, string name)
    {
        Console.WriteLine("Posting {0} message to '{1}'...", name, p.ProcessName);

        bool succeeded = PostMessage(p.MainWindowHandle.ToInt32(), message, IntPtr.Zero, IntPtr.Zero);

        Console.WriteLine("Posted {0} message to '{1}' (succeeded:{2}).", name, p.ProcessName, succeeded);
    }
} 

The worker thread Id you are seeing is 0. This is a framework thread and is expected - it is not a thread that the program has "spawned". If you attach to any .Net process you will see this. I'm not sure which framework thread it is - definitely not the finalizer thread as this is never thread 0. Perhaps a JIT thread?

What is more interesting then is your main thread, as this appears to be hanging. I would concentrate on debugging your main thread to bottom this out. Has it deadlocked itself from a window closed event handler waiting for something that will never happen for example?

Update

After reading the stack trace for the main thread added to the question, it would be interesting to run a test to bottom out whether the main thread is stalled or whether it is simply idle waiting for a message (and that it is waiting for a WM_CLOSE that it never received or was never sent).

The code below can be used to manually send a WM_CLOSE message to your application. Simply wait for your program to hang when you have shut it down, and then run the code. Replace the process name with your own.

Update 2

Ok, it looks like the main thread is well and truly hung as it is not processing WM_CLOSE or WM_QUIT messages.

Please try and make the smallest application that can reproduce the issue and post the code.

Example WM_CLOSE\WM_QUIT App

internal class Program
{
    private const int WM_QUIT = 0x0012;
    private const int WM_CLOSE = 0x0010;

    [DllImport("user32.dll")]
    private static extern bool PostMessage(int hhwnd, uint msg, IntPtr wParam, IntPtr lParam);

    private static void Main()
    {
        Process p = GetProcess("Your process name - no '.exe' required");

        CloseMainWindow(p);
    }

    private static Process GetProcess(string name)
    {
        List<Process> processes = Process.GetProcessesByName(name).ToList();

        if (processes.Count != 1)
        {
            throw new Exception(
              "Expected 1 process with name '" + name +
              "' but found " + processes.Count + ".");
        }

        return processes[0];
    }

    private static void CloseMainWindow(Process p)
    {
        PostMessage(p, WM_CLOSE, "close");
    }

    private static void QuitApplication(Process p)
    {
        PostMessage(p, WM_QUIT, "quit");
    }

    private static void PostMessage(Process p, uint message, string name)
    {
        Console.WriteLine("Posting {0} message to '{1}'...", name, p.ProcessName);

        bool succeeded = PostMessage(p.MainWindowHandle.ToInt32(), message, IntPtr.Zero, IntPtr.Zero);

        Console.WriteLine("Posted {0} message to '{1}' (succeeded:{2}).", name, p.ProcessName, succeeded);
    }
} 
野味少女 2024-10-15 20:30:44

如果您要创建工作线程(而不是池线程),请设置其 名称 描述创作的内容。

If you are creating the worker threads (rather than pool threads), set their Name to something descriptive on creation.

旧时光的容颜 2024-10-15 20:30:44

将日志记录添加到所有代码中以记录方法和线程 ID。您可以使用正则表达式替换将其放在每个方法的开头。然后运行应用程序并关闭。查看哪些日志消息与未关闭的工作线程具有相同的线程 id。

Add logging to all your code to log the method and thread ID. You can use a regex replace to put it at the start of every single method. Then run the app and shut down. See what log messages have the same thread id as the worker thread that doesn't shut down.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文