4

I am in a situation where I have to spawn a new thread manually, so I am able to can call .SetApartmentState(ApartmentState.STA). This means (as far as I know) that I cannot use Task. But I would like to know when the thread was done running, something like the await which works with async. However, the best I can come up with is a loop, constantly checking Thread.IsAlive, like this:

var thread = new Thread(() =>
{ 
    // my code here 
});

thread.SetApartmentState(ApartmentState.STA);
thread.Start();

while(thread.IsAlive)
{
    // Wait 100 ms
    Thread.Sleep(100);
}

This should work (as long as the thread don't end up stalling), but it seems kind of clumsy. Isn't there a more clever way to check when the thread is done (or dead)?

It is only to avoid blocking the GUI thread, so any minor performance hits are fine (like some hundred milliseconds).

Jakob Busk Sørensen
  • 5,599
  • 7
  • 44
  • 96
  • 3
    You are looking for a really weird solution, instead, explain what problem you're trying to solve. – aybe Nov 14 '19 at 21:08
  • 4
    `Thread.Join()`? – Ian Mercer Nov 14 '19 at 21:09
  • 1
    What I am trying to do is essentially `await Task.Run()`. But I cannot do that, due to the STA constraint. I can do `Thread.Join()` but doesn’t that block the UI thread in the meanwhile? – Jakob Busk Sørensen Nov 14 '19 at 21:14
  • I forgot the ApartmentState part in my example. I have added it now. My bad. – Jakob Busk Sørensen Nov 14 '19 at 21:22
  • 2
    Would your actual issue be solved by this question? https://stackoverflow.com/questions/16720496/set-apartmentstate-on-a-task – yaakov Nov 14 '19 at 21:24
  • `Thread.Join()` would block the UI only if Thread is the UI thread. – Soleil Nov 14 '19 at 22:52
  • What you are trying to do here is extremely dangerous. You have not told us why you wrote this code, but very high odds that you're trying to suppress an exception, telling you that your code is not thread-safe. It just shoot the messenger, it truly is unsafe and you won't have a shot at debugging the occasional deadlock or rendering glitch. The kind that notoriously don't happen when you debug. You must use Task.ContinueWith(), passing the TaskScheduler.FromCurrentSynchronizationContext as the scheduler to get it right. – Hans Passant Nov 15 '19 at 11:08
  • @HansPassant I am not trying to suppress anything, I just thought that I could not use `Task`, since I needed `Thread.SetApartmentState(ApartmentState.STA)` :-). Following up on this post, I ended up finding an even smarter way to do this: https://stackoverflow.com/questions/58873865/catch-exception-after-thread-is-done/58874227?noredirect=1#comment104018797_58874227 – Jakob Busk Sørensen Nov 15 '19 at 11:38

2 Answers2

1

Here is an extension method you could use to enable the awaiting of threads (inspired from this article: await anything).

public static TaskAwaiter GetAwaiter(this Thread thread)
{
    return Task.Run(async () =>
    {
        while (thread.IsAlive)
        {
            await Task.Delay(100).ConfigureAwait(false);
        }
    }).GetAwaiter();
}

Usage example:

var thread = new Thread(() =>
{ 
    Thread.Sleep(1000); // Simulate some background work
});
thread.IsBackground = true;
thread.Start();
await thread; // Wait asynchronously until the thread is completed
thread.Join(); // If you want to be extra sure that the thread has finished
Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
  • Really nice idea. Do you think it would be possibe to include `thread.SetApartmentState(ApartmentState.STA)` and `thread.Start()` in there somehow? So it can be run in a similar manner to a traditional async method... – Jakob Busk Sørensen Nov 15 '19 at 08:23
  • The `GetAwaiter` method just enables the `await` operator for threads. The creation of the thread with its desired properties can be done as usual. I updated my answer with a complete example. – Theodor Zoulias Nov 15 '19 at 10:26
-1

Could you use the BackgroundWorker class? It has an event that reports when its finished.

Lift
  • 546
  • 2
  • 4
  • 24