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

Await Task hangs if creating a new form instance before running the task

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

如果我注释行 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();
    }

您的代码从未调用 Application.Run() 来启动应用程序的消息循环。

当您使用 await 时,延续可能会根据上下文发布到消息循环中。一旦你创建了一个表单,你就处于这样的环境中,你必须确保消息循环实际运行。

通常调用 Application.Run() 的 WinForms 应用程序的初始化代码位不是使用 async/await.

的好地方

My await code hangs if I run a create a new form instance before running the await code.

嗯,这有很多原因。

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

  2. 控制台应用程序默认为 MTA 而不是 STASTA 需要 所有应用 运行 GUI

  3. 控制台应用程序不处理 Windows 消息泵,不像原生 windows 或 WinForms 应用程序

当然可以将消息泵添加到控制台应用程序;强制使用 STA 线程,但是当 Visual Studio 中有一个非常好的 WinForms 向导时,你为什么要这么做?

I comment the line Form frm = new Form(); the code will be run properly

我真的很怀疑。你可能有一个 window appear 但它肯定不会是 interactive。除非处理消息泵,否则 window 不会绘制 (包括在它前面的 window 被移走后绘制)。单击按钮和菜单无响应计时器不起作用

最后,不要尝试通过线程 and/or Task 创建 GUI,因为您将无法控制 STA。这一切都必须从应用程序的主线程完成,如前所述,主线程必须是 STA。没有理由要多个 STA

总而言之,在 window.

的构造过程中使用 async/await 没有什么意义

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

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

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

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

最终,看起来构建您的应用程序的人已经考虑过应该如何对其进行单元测试。你或许应该去和他们谈谈,以全面了解他们的意图。