Consider the snippet bellow.
A form adds a BusyForm to the queue and later on, when the form is done, it sets the result for the TCS, marking it as complete, without removing the item from the queue.
After a while, something happens and we need to wait for all busy forms, so we await each TCS task
in the queue. But surprise, surprise .. GC grabbed it.
Why? Isn't it referenced by the queue?
static class Global
{
public static ConcurrentQueue<BusyForm> BusyForms = new ConcurrentQueue<BusyForm>();
public class BusyForm
{
public string Name;
public TaskCompletionSource<bool> Done = new TaskCompletionSource<bool>();
public BusyForm(string name)
{
this.Name = name;
}
}
}
// somewhere else when form is done
busyForm.Done.SetResult(true);
// after a while, when required
while(!Global.BusyForms.IsEmpty)
{
Global.BusyForms.TryPeek(out busyForm);
await busyForm.Done.Task; // can be disposed by GC if finished a long time ago
Global.BusyForms.TryDequeue(out busyForm);
}
It's not clear how this all fits together, but it seems like your process should just be:
while(!Global.BusyForms.IsEmpty)
{
if(Global.BusyForms.TryDequeue(out busyForm))
await busyForm.Done.Task; // can be disposed by GC if finished a long time ago
else
{
// could not dequeue for some reason - need to break the loop so that we no not get in an infinite loop.
break;
// or perhaps throw an exception?
}
}
That way there is no risk of the same Task
being processed twice. It seems odd to process an item while it's still on the queue. Think about checking in at the airport - you don't check in while you're in line - you get pulled out of line to be served by the next available agent.