0

Performing a time consuming task inside a WinForm application is better done wrapping it inside another thread.

As Thread.Join() blocks the main thread is there anything wrong using this kind of approach with Application.DoEvents?

Thread t = new Thread(() => {...});
t.Start();
while (t.IsAlive) Application.DoEvents();
t.Join();

Edit: I don't want to stop the execution of main thread, in fact I have an IOleMessageFilter running on the STAThread. This is also the reason why I thought that DoEvents makes sense.

piertoni
  • 1,933
  • 1
  • 18
  • 30
  • 1
    Yes, it can cause nasty and incredibly difficult to diagnose re-entry bugs. – Matthew Watson Feb 14 '22 at 11:48
  • You should be using `async`, `await` and `Task`. – Matthew Watson Feb 14 '22 at 11:49
  • From the documentation on DoEvents(): "Calling this method causes the current thread to be suspended while all waiting window messages are processed. If a message causes an event to be triggered, then other areas of your application code may execute. This can cause your application to exhibit unexpected behaviors that are difficult to debug. If you perform operations or computations that take a long time, it is often preferable to perform those operations on a new thread. " – Matthew Watson Feb 14 '22 at 11:50
  • If the background work takes some time you might want to consider showing a "please wait" dialog while doing it. That would prevent user input while doing the processing, preventing the user from starting multiple background tasks or other potential issues. – JonasH Feb 14 '22 at 12:22
  • Never ever ever ever use `DoEvents()`. It is evil (as described above in the comments). It is one of the best ways to insert insidiously difficult-to-debug bugs into your code. Don't use it. – Enigmativity Feb 14 '22 at 20:46

1 Answers1

2

The while (t.IsAlive) Application.DoEvents(); is a tight loop, that will convert one core of your machine to a heat generator while the other thread is running. As of February 2022, the recommended way to launch a background work and suspend the execution of the current method until this work is done, is to use async/await and the Task.Run method.

private async void Button1_Click(object sender, EventArgs args)
{
    var result = await Task.Run(() => SomeCalculation());

    // Here do something with the result of the calculation
}

The SomeCalculation method will be invoked on a ThreadPool thread, and the UI will remain responsive during the invocation. If you have some particular reason to invoke it on a dedicated thread, you can use the lower level Task.Factory.StartNew method, configured with the TaskCreationOptions.LongRunning option:

var result = await Task.Factory.StartNew(() => SomeCalculation(),
    CancellationToken.None, TaskCreationOptions.LongRunning,
    TaskScheduler.Default);
Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
  • 2
    Just a small note: Inside `Button1_Click()` it would be advisable to call `Button1.Enabled = false;` before the `await` and then `Button1.Enabled = true;` after the `await`, to prevent the user spamming the button and causing multiple calls. – Matthew Watson Feb 14 '22 at 12:58