Reputation: 142
I have run into some legacy code Winforms code that basically does this...
private static bool _connected = false;
private static bool _closing = false;
static void Main(string[] args)
{
// Stuff
ThreadPool.QueueUserWorkItem(ThreadProc, null);
// More stuff
while (!_closing)
{
if (_connected)
{
break;
}
// Do stuff
Application.DoEvents();
Thread.Sleep(100);
// Do stuff
}
if (_connected)
{
// Finally...
Application.Run(new MyForm());
}
}
private static void ThreadProc(object obj)
{
while (!_closing)
{
// Stuff
if (!Method2())
{
// Error
_closing = true;
return;
}
else
{
/// Success
break;
}
}
_connected = true;
}
private static async Task<SomeData> Method1()
{
// There is a splash screen that gets displayed so we are
// waiting for it to be available
while (Application.OpenForms.Count == 0) Thread.Sleep(100);
// So this gets run on the Windows GUI thread
SomeData result = await (Application.OpenForms[0].Invoke(new Func<Task<SomeData>>(() => { SomethingAsync())));
return result;
}
private static bool Method2()
{
TaskAwaiter<SomeData> awaiter = Method1().GetAwaiter();
while (!awaiter.IsCompleted)
{
// This appears to be why this code is done this way...
// to not block Windows events while waiting.
Application.DoEvents();
Thread.Sleep(100);
}
var SomeData = awaiter.GetResult();
if (result == null) { return false; } // error
// do something with result
return true;
}
I have read up a bit on the negatives on using GetAwaiter().GetResult()
and I'm searching for a better solution. I believe the reason this was done this way was so application events didn't block while Method1()
executes since we are checking IsCompleted
on the awaiter and once it is complete, we then get the result.
Is this a legit usage of GetAwaiter()
and GetResult()
or is there a better solution?
The above legacy code works and I am unsure as to whether this is a "good" solution to the problem or is a timebomb waiting to happen.
Clarification: The Method2
is called from a thread that was created using ThreadPool.QueueUserWorkItem() from inside the static Main()
method, before the Application.Run()
which is not called until after the thread terminates and is thus called on the same thread that entered the static Main()
method. I have attempted to update the code with a simplified version of what I am dealing with that showcases the additional thread and when Application.Run() is invoked.
Purpose The purpose appears to be that when the application runs, it creates a thread that calls Method1 which basically does user login/authentication. Once authenticated, it attempts to connect to a server process using a JWT. The Main() thread is looping to wait for the connection thread to either succeed or error out while also calling Application.DoEvents(). I don't quite understand the purpose of the second thread.
Upvotes: 1
Views: 1294
Reputation: 72279
No this is not the right way to go about this. You are running an infinite loop on a UI thread, putting it to sleep for 100ms, and then forcing it to wake and handle events then putting it to sleep again.
It is not a "time-bomb waiting to happen", but it is an inefficient method of waiting.
Instead, just use either async Task
or async void
which handles the waiting for you, but does not block the UI thread at all. It puts the continuation of the method (everything after the await
) on a callback which only gets called once the await
completes.
Note that async void
should only be used in event handlers coming off the UI, otherwise use async Task
and friends.
private static async Task Method2()
// alternatively
private static async void Method2()
{
var data = await Method1();
// do something with result
}
Upvotes: 2