Search code examples
c#winformstaskdeadlock

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


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

If I comment the line Form frm = new Form(); the code will be run properly otherwise it will hang in the code await Task.Delay(2000);.

Another solution is to create a new form instance using Task.Run(the commented line in my example code). I have no idea why it does work and doesn't know if that's fine to create a new form instance in a subthread.

Here is a simple example code to replicate the problem. Is there anyone have the idea why does this happen?

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);
        }
    }
}

Sorry for the confusion. Added the real code I am writing which is actually the unit test code.

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

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

Solution

  • The other answers saying that you shouldn't be creating forms inside a console app or a unit test are absolutely correct.

    From your updated code, it looks like someone has already gone to the trouble of making sure you can unit-test your presenter without having to instantiate your form: the FrmLoginPresenter constructor takes an IFrmLogin, which I'm assuming is an interface implemented by FrmLogin. This abstraction exists to allow you to unit-test FrmLoginPresenter without having to create an actual FrmLogin.

    What you want to do is to create a mock implementation of IFrmLogin. This might be a normal class which you write yourself which implements IFrmLogin (it might already exist in your test suite), or it might use a mocking library like Moq or Rhino Mocks.

    Then you pass your mock implementation to the FrmLoginPresenter constructor.

    Ultimately, it looks like whoever architected your application has already thought about how it should be unit-tested. You should probably go and speak to them to get the full picture on their intentions.