【问题标题】:Windows forms application blocks after station lock站点锁定后 Windows 窗体应用程序块
【发布时间】:2010-03-22 13:05:21
【问题描述】:

我们在工作中遇到了一个严重的问题。我们发现,在客户端运行的站点被锁定/解锁后,客户端被阻止。没有重绘。所以 UI 线程被某些东西阻塞了。使用 windbg 查看 UI 线程(线程 0)的调用堆栈,我们看到引发了 UserPreferenceChanged 事件。它通过 WindowsFormsSynchronizationContext 使用它的 controlToSend 字段编组到 UI。它被对编组控制的调用阻止。调用的方法是 MarshaledInvoke 它构建了一个

ThreadMethodEntry entry = new ThreadMethodEntry(caller, method, args, synchronous, executionContext);

这个条目应该会变魔术。该调用是同步调用,因此(仍在 Control 类的 MarshaledInvoke 中)等待调用:

if (!entry.IsCompleted)
{
    this.WaitForWaitHandle(entry.AsyncWaitHandle);
}

我可以在堆栈上看到的最后一件事是在前面提到的 AsyncWaitHandle 上调用的 WaitOne。 这很烦人,因为只有运行时的调用堆栈而不是调用我们的方法之一,我们无法真正指出代码中的错误。 我可能错了,但我猜封送控制不是“封送”到 ui 线程。但是另一个......我真的不知道是哪一个,因为其他线程正在被我们使用并且被阻止......也许这就是问题所在。但是其他线程都没有运行消息循环。这很烦人。 过去,我们在将控件编组到正确的 ui 线程时遇到了一些问题。那是因为构造的第一个表单是启动表单。这不是主要形式。我们曾经使用主窗体来编组对 ui 线程的调用。但有时,一些调用会转到非 ui 线程,一些网格会中断,上面有一个大的红色 X。我通过创建一个特定的类来解决这个问题:

public class WindowsFormsSynchronizer
{
    private static readonly WindowsFormsSynchronizationContext = new WindowsFormsSynchronizationContext();
//Methods are following that would build the same interface of the synchronization context.
}

这个类被构建为第一种形式的第一个对象。

我们注意到了一些其他奇怪的事情。查看堆有 7 个 WindowsFormsSynchronizationContext 对象。其中 6 个具有相同的 controlToSend 实例,而另一个具有一些不同的 controlToSend 实例。最后一个是应该编组对 UI 的调用的那个。

我没有任何其他想法...也许你们中的一些人有同样的问题?

【问题讨论】:

    标签: .net windows winforms user-interface


    【解决方案1】:

    是的,当程序违反 Windows 线程规则时,SystemEvents 类(此处为 SessionSwitch)生成的事件很容易导致死锁。我不清楚你的情况究竟是如何发生的,但在我看来,你已经与线程问题作斗争了一段时间。

    .NET 2.0 对此具有内置诊断功能。请务必利用这一点,它仅适用于调试器。确保您没有将 Control.CheckForIllegalCrossThreadCalls 设置为 false。

    【讨论】:

    • 不知道 Control.CheckForIllegalCrossThreadCalls。我会调查的。
    【解决方案2】:

    虽然这可能无法具体解决您的问题,但您可以查看其他线程的调用堆栈(假设您在 Visual Studio 中进行调试)。有一个名为“Threads”的调试窗口(在默认菜单配置中,Debug->Debug Windows->Threads)。有了这个可见,您可以双击任何线程以切换到它。这将加载该线程的调用堆栈,并且应该希望为您提供更多关于正在发生的事情的信息。

    【讨论】:

      【解决方案3】:

      昨天我们确实杀死了怪物。我最初的预感是应用程序启动表单给出的错误行为是启动表单,后来切换为真正的主表单。问题是 2-4 个月前,启动画面逐渐消失。这是使用计时器完成的。但是闪屏用于登录和建立一些缓存和加载一些用户偏好。其中一些行动涉及一些控制的构建。为了使初始屏幕消失,透明度会不时更改为较低的值。这是在 UI 线程上处理的,其他耗时的调用被发送到 ThreadPool。这涉及在其他线程上创建控件。出于某种原因,其中一些控件被用于封送回 UI 线程。无论如何,通过将这些调用重新编组到 UI 线程来修复它。

      【讨论】:

        猜你喜欢
        • 2016-10-03
        • 2014-08-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多