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).
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)
{
ArgumentNullException.ThrowIfNull(thread);
return Task.Run(async () =>
{
using PeriodicTimer timer = new(TimeSpan.FromMilliseconds(100));
while (thread.IsAlive) await timer.WaitForNextTickAsync();
thread.Join(); // Let's be extra sure that the thread has finished
}).GetAwaiter();
}
This implementation pools the IsAlive
property of the thread every 100 milliseconds, using a PeriodicTimer
(.NET 6). The implication is that the awaiting is not completely passive (there is some overhead), and the completion of the awaitable is not instantaneous. The gap between the termination of the thread and the completion of the awaitable will normally be under 100 milliseconds, but it could grow larger in case the ThreadPool
is saturated (the PeriodicTimer
relies on the ThreadPool
for ticking its events).
Usage example:
Thread thread = new(() =>
{
Thread.Sleep(1000); // Simulate some background work
});
thread.IsBackground = true;
thread.Start();
await thread; // Wait asynchronously until the thread is completed
For a GetAwaiter
version that doesn't depend on the PeriodicTimer
class, and so it can be used with .NET versions earlier than .NET 6, see the 3rd revision of this answer.