【问题标题】:Threading in Form_Load - Application now hangs suddenlyForm_Load 中的线程 - 应用程序现在突然挂起
【发布时间】:2012-02-22 21:52:13
【问题描述】:

抱歉,这篇文章太长了,我只是想尽可能地说明我的情况。 如果您想快速了解问题的要点,请阅读粗体字并检查代码。

我使用 ClickOnce 部署 C# 应用程序,并选择让我的应用程序使用 ApplicationDeployment Class 手动检查更新,而不是让它为我检查更新。

该程序是一个专门的网络扫描程序,用于搜索我工作的公司制造的网络设备。加载主窗口后,会显示一个提示,询问用户是否要扫描网络。如果他们说是,扫描开始,可能需要一两分钟才能完成,具体取决于他们的网络设置;否则它只是等待用户执行一些操作。

我在Form_Load 中做的最后一件事是创建一个检查更新的新线程。通过大约 12 个版本,这几个月来一直运行良好,但突然停止工作。我根本没有更改更新代码,也没有更改应用启动时发生的顺序。

看着代码,我想我明白为什么它不能正常工作,并想确认我的想法是否正确。如果是这样,那就引出了一个问题,为什么它以前确实有效——但我也不太关心这个问题。

考虑以下代码:

frmMain.cs

private void Form1_Load(object sender, EventArgs e)
{
    // set up ui, load settings etc

    Thread t = new Thread(new ParameterizedThreadStart(StartUpdateThread));
    t.Start(this);
}

private void StartUpdateThread(object param)
{
    IWin32Window owner = param as IWin32Window;

    frmAppUpdater.CheckForUpdate(owner);
}

frmAppUpdater.cs

public static void CheckForUpdate(IWin32Window owner)
{
    if (ApplicationDeployment.IsNetworkDeployed) {
        Console.WriteLine("Going to check for application updates.");
        parentWindow = owner;

        ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
        ad.CheckForUpdateCompleted += new CheckForUpdateCompletedEventHandler(ad_CheckForUpdateCompleted);
        ad.CheckForUpdateProgressChanged += new DeploymentProgressChangedEventHandler(ad_CheckForUpdateProgressChanged);

        ad.CheckForUpdateAsync();
        // CAN/WILL THE THREAD CREATED IN FORM1_LOAD BE TERMINATED HERE???
    }
}

CheckForUpdateAsync() 回调完成时,如果没有可用的更新,则方法调用简单地返回;如果有可用的更新,我会使用一个循环来阻止,直到发生 2 件事:用户已关闭“您要扫描提示”并且当前未运行任何扫描。

循环看起来像这样,发生在ad_CheckForUpdateCompleted:

while (AppGlobals.ScanInProgress || AppGlobals.ScanPromptVisible) {
    System.Threading.Thread.Sleep(5000);
}

我睡了 5 秒钟,因为我认为这是在一个单独的线程中发生的,并且它似乎运行了一段时间。

我对上述代码的主要问题是:
当从CheckForUpdate 调用ad.CheckForUpdateAsync(); 时,我在Form1_Load 中创建的线程是否终止(或者它可能终止)?我怀疑可能是因为随后的Async调用导致方法返回,然后启动另一个线程?

我感到困惑的唯一原因是因为这个方法在没有挂起应用程序的情况下工作了很长时间,现在它突然挂起,我尽最大努力调试发现是 Sleep 调用阻塞了应用程序。

如果有帮助,我很乐意发布 frmAppUpdater.cs 的完整代码。

【问题讨论】:

  • 我根本没有更新 Visual Studio 或更改应用程序的目标 .NET 版本,但问题可能只发生在具有 .NET 4 客户端配置文件 SP1 的机器上。我现在正在调查。我看到问题的安装版本是4.0.30319。我将尝试查看 .NET 4 SP(0) 上是否没有出现此问题。

标签: c# winforms multithreading clickonce


【解决方案1】:

当从 CheckForUpdate 调用 ad.CheckForUpdateAsync(); 时 我在 Form1_Load 中创建的线程终止(或可能终止)?

如果CheckForUpdateAsync() 调用是异步的,那么是的,线程将终止,否则不会。

如果您怀疑Sleep 导致应用程序挂起,那么AppGlobals.ScanInProgressAppGlobals.ScanPromptVisible 这两个变量可能总是设置为true!您应该开始查看将它们设置为 true 的代码,看看那里发生了什么。

为了避免应用程序挂起,您可以引入一个变量来避免无限期休眠:

int nTrials = 0;
while ((AppGlobals.ScanInProgress || AppGlobals.ScanPromptVisible) && (nTrials < 5)) {
    System.Threading.Thread.Sleep(5000);
    nTrials++;
}
// Check the results and act accordingly

我个人不喜欢使用Sleep 进行线程同步。 .NET 提供了一堆非常适合线程同步的classesWaitHandle 就是其中之一。

【讨论】:

  • 据我所知,该程序甚至没有走那么远 - 如果它做到了,它现在正在主 UI 线程中运行该循环,而不是之前似乎运行的任何地方我们没有遇到这个问题。这些值确实会更改为 false,但现在应用程序挂起它永远不会那么远,因为它不会响应是/否提示上的输入。
  • System.Threading.Thread.Sleep(5000); 永远不应该在主 UI 线程中执行,因为它会导致您的应用程序至少挂起 5 秒(如果它只循环一次)
  • 据我了解,它之前是从一个单独的线程运行的,因为它从未导致应用程序挂起,即使它在那一点等待应用程序终止几分钟。出于某种原因,某些东西改变了应用程序和线程的行为。我同意我应该使用 WaitHandle。感谢您指出线程终止,这一定是正在发生的事情,但由于某种原因之前不会立即发生。
  • 如果您想确认代码确实在 UI 线程中运行,如果返回 true,只需调用 InvokeRequired(),那么您不在 UI 线程上并且 UI 线程正在挂起其他一些原因。
【解决方案2】:

Asynchronous Delegates Vs Thread/ThreadPool?看到这篇文章

您的表单加载方法似乎在做同步工作。您提到您正在使用 clickonce 部署。在上一个版本之后二进制位置是否已更改或对此资源的权限已更改。看起来线程中的工作(检查更新)永远不会完成,也永远不会交还给表单。

作为一个即时修复,我会将线程方法更改为委托 - 如果您使用委托,那么这将不再是客户问题(表单将响应最终用户)但根本问题仍然存在。

下一步,我将通过http://msdn.microsoft.com/en-us/library/ms229001.aspx 进行故障排除

【讨论】:

  • 谢谢,我会调查的。我向 Wireshark 确认,新版本的应用程序清单和部署文件确实会被挂起的应用程序下载。我认为线程现在正在加入主线程,而之前由于某种原因没有。
  • clickonce 应用程序的二进制文件和权限没有改变 - 因为我确认正在下载清单,ad.CheckForUpdateAsync(); 现在让我认为线程已终止,现在异步回调正在发生在主线程以前不是。我正在调查这是否是由于 .NET 4 SP1 更新造成的。
猜你喜欢
  • 2014-01-13
  • 2021-07-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-13
  • 1970-01-01
相关资源
最近更新 更多