如果我在运行await代码之前运行create a new form示例,await代码将挂起。
如果我注解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();
}
4条答案
按热度按时间g9icjywg1#
其他回答说您不应该在控制台应用程序或单元测试中创建窗体,这是绝对正确的。
从更新后的代码来看,似乎有人已经不厌其烦地确保您可以对演示者进行单元测试,而不必示例化表单:
FrmLoginPresenter
构造函数接受一个IFrmLogin
,我假设它是一个由FrmLogin
实现的接口。你要做的是创建一个
IFrmLogin
的模拟实现,这可能是你自己编写的一个实现IFrmLogin
的普通类(它可能已经存在于你的测试套件中),或者它可能使用一个模拟库,比如Moq或Rhino Mocks。然后将mock实现传递给
FrmLoginPresenter
构造函数。最后,看起来无论是谁设计了你的应用程序,他都已经考虑过应该如何进行单元测试了。你可能应该去和他们谈谈,以了解他们的意图。
uwopmtnx2#
您的代码从未调用Application.Run()以启动应用程序的消息循环。
当你使用
await
时,根据上下文的不同,continuation可能会被发送到消息循环中。一旦你创建了一个表单,你就处于这样一个上下文中,你必须确保消息循环确实运行。WinForms应用程序的初始化代码中通常调用Application.Run()的部分不适合使用async/await。
s4n0splo3#
如果我在运行await代码之前运行create a new form示例,await代码将挂起。
这有很多原因。
1.您的应用程序实际上是 * 控制台应用程序 * 而不是WinForms 应用程序。您正在尝试在控制台应用程序中显示GUI,这通常是不可能的
1.控制台应用程序默认为
MTA
,而不是STA
。STA
是运行GUI的所有应用程序必需的1.控制台应用程序不处理 *Windows消息泵 *,这与本机窗口或WinForms应用程序不同
当然,您可以添加一个消息泵到控制台应用程序;强制STA线程,但是当Visual Studio中有一个非常好的WinForms向导时,为什么还要强制STA线程呢?
我注解了行Form frm = new Form();代码将正确运行
我真的很怀疑。你可能会有一个窗口 * 出现 *,但它肯定不会是 * 交互式 *。除非消息泵被处理,否则窗口不会绘制(包括在它前面的窗口被移走后绘制)。单击按钮和菜单将无响应。计时器将无效。
最后,不要试图通过线程和/或
Task
创建GUI,因为您对STA
几乎没有控制权。这一切都必须通过应用程序的主线程来完成,主线程必须是前面提到的STA
。没有理由使用多个STA
。总之,在构造窗口时使用
async/await
没有什么意义。f5emj3cl4#
我有点晚了,但我发现自己遇到了同样的问题(尽管我的用例是一个可以作为无窗口进程或GUI运行的应用程序)。
执行HTTP异步操作似乎是一个大问题(Task.Delay没有导致死锁/挂起)。
正如其他人所说,在Application.Run之前运行异步代码会导致问题。下一次尝试是通过将Application.Run()粘贴在构造函数方法中来触发它内部的异步代码。
正如您所预料的那样,上面的方法也失败了,显然表单实际上必须在Application.Run发挥作用之前创建。
因此,将代码粘贴在form_load事件中是最后起作用的
对于我的用例,我希望代码运行但不显示窗体。任何设置.visible的尝试都会被覆盖,因此我通过其他方法将其设置为不可见。上面的代码块成功地运行了异步代码,而没有触发死锁