如果在运行任务之前创建新的表单实例,则等待任务挂起

时间:2019-03-01 11:20:18

标签: c# winforms task deadlock

如果在运行等待代码之前运行创建新表单实例,我的等待代码将挂起。

如果我在行Form frm = new Form();中添加注释,则代码将正确运行,否则将挂在代码await Task.Delay(2000);中。

另一种解决方案是使用Task.Run(我的示例代码中的注释行)创建一个新的表单实例。我不知道为什么它能工作,也不知道在子线程中创建新的表单实例是否合适。

这是一个简单的示例代码,可以重现该问题。有谁知道为什么会这样?

using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ConsoleApp1
{
    class Program
    {
        public async static Task Main(string[] args)
        {
            //Form frm = await Task.Run( () => new Form());
            Form frm = new Form();
            await Delay();         
        }

        public static async Task Delay()
        {
            await Task.Delay(2000);
        }
    }
}

很抱歉造成混乱。添加了我正在编写的实际代码,实际上是单元测试代码。

    public async Task TestFrmLoginGen1()
    {
        IFrmLogin frmLogin;
        frmLogin = await Task.Run(() => new FrmLogin());
        //frmLogin = new FrmLogin();

        FrmLoginPresenter loginPresenter = new FrmLoginPresenter(frmLogin);
        await  loginPresenter.LoginAsync();
    }

3 个答案:

答案 0 :(得分:0)

您的代码永远不会调用Application.Run()来启动应用程序的消息循环。

使用await时,根据上下文的不同,延续可能会发布到消息循环中。创建表单后,您将处在这样的环境中,并且必须确保消息循环真正运行。

通常调用Application.Run()的WinForms应用程序的初始化代码不是使用异步/等待的好地方。

答案 1 :(得分:0)

  

如果在运行等待代码之前运行创建新表单实例,我的等待代码将挂起。

有很多原因。

  1. 您的应用实际上是控制台应用,而不是 WinForms 应用。您正在尝试在控制台应用程序中显示GUI,这通常是不可能的

  2. 控制台应用程序默认为MTA而不是STA。对于运行GUI的所有应用,要求STA

  3. 与本地Windows或WinForms应用不同,控制台应用不会处理 Windows消息泵

确定可以将消息泵添加到控制台应用程序;强制使用STA线程,但是为什么在Visual Studio中有一个完美的WinForms向导时您会为什么呢?

  

我注释Form frm = new Form();行;代码将正常运行

我真的对此表示怀疑。您可能会出现一个 窗口,但肯定不会 interactive 。除非处理消息泵,否则窗口不会绘画(包括移开位于其前面的窗口之后的绘画)。点击按钮和菜单无响应计时器无效

最后,请勿尝试通过线程和/或Task创建GUI,因为您几乎无法控制STA。如上所述,所有操作都必须从应用程序的主线程完成,该主线程必须为STA。几乎没有理由重复多个STA

最后,在构建窗口期间使用async/await毫无意义。

答案 2 :(得分:0)

其他答案表明您不应该在控制台应用程序或单元测试中创建表单是绝对正确的。

从更新后的代码看来,似乎已经有人麻烦确保您可以对演示者进行单元测试而不必实例化表单:FrmLoginPresenter构造函数采用IFrmLogin,我假设是由FrmLogin实现的接口。这种抽象的存在使您可以对FrmLoginPresenter进行单元测试,而不必创建实际的FrmLogin

您想要做的是创建IFrmLogin的模拟实现。这可能是您自己编写的实现IFrmLogin的普通类(它可能已经存在于您的测试套件中),或者可能使用了Moq或Rhino Mocks之类的模拟库。

然后将模拟实现传递给FrmLoginPresenter构造函数。

最终,看起来设计您的应用程序的任何人都已经考虑过应该如何对其进行单元测试。您可能应该去与他们交谈,以全面了解他们的意图。