OneHoopyFrood
OneHoopyFrood

Reputation: 3959

End thread when reference to object is dropped

I have a List of objects that sometimes spawn a thread and maintain a bool that terminates said thread; however, the references for these objects are occasionally dropped when the parent List is cleared via List.clear().

This obviously results in orphaned threads that I can no longer terminate.

I know that I can solve the problem by making sure that all threads are canceled before clearing the List, but I'm curious if it's possible to determine if the reference to the object that spawned the thread is dropped.

Basically I've got this common structure:

List<Foo> myFoos = new List<Foo>();

class Foo {
    public bool keepThreadAlive = false;

    public void StartThread () {
        Thread newThread = new Thread(() => {
            while(keepThreadAlive) {
                // Do stuff until told to stop
            }
        });
    }
}

Where myFoos is cleared occasionally.

Is it possible to sense the loss of reference to Foo from within the unnamed thread and terminate the while?

Upvotes: 3

Views: 245

Answers (1)

Peter Duniho
Peter Duniho

Reputation: 70661

I'm curious if it's possible to determine if the reference to the object that spawned the thread is dropped

It is not, not in a direct sense. .NET has a WeakReference<T> class that can track whether an object is still referenced by any other variable, but in this case it's unlikely you'd be able to create a situation where the list contains the only variable referencing the object. I.e. as long as any variable still references the object, the WeakReference<T> object will indicate that the object is still alive.

In the code example you've provided here, your thread is running code inside the object being referenced in the list. You're using an anonymous method, but assuming that code uses the object itself, the anonymous method will be contained in an anonymous class that itself references the object, causing a second, persistent reference.

You could try to bypass that by putting the thread code into a static method, where the object reference itself is retrieved from the WeakReference<T> object if available (and the thread would exit when not available). But assuming the thread is using the object with any sort of regularity, it's likely the code in the static method would keep the object alive for long enough periods of time that the GC wouldn't have a chance to notice it in the unused state.

As commenter user2864740 notes, there are other, more explicit approaches you can take. I'm not very fond of the idea of polling the list (this can be especially bad if the list can be large, and is wasteful in any case), but if you are able to use an observable list, such as ObservableCollection<T>, that could work. To do that, you've have to subscribe to the CollectionChanged event and handle it appropriately:

myFoos.CollectionChanged += (sender, e) =>
{
    if (e.Action == NotifyCollectionChangedAction.Remove ||
        e.Action == NotifyCollectionChangedAction.Replace)
    {
        foreach (Foo foo in e.OldItems)
        {
            foo.CancelThread(); // or whatever
        }
    }
};

Note that the above is just the basic idea. There's not enough context in your question to know whether it would suffice or if, for example, you might need to do something special to handle new items being added to the collection (either via Add or Replace action).

Personally, it seems to me that if you know these items are only being "lost" when you clear the list, that the best approach is the one you already know about: just cancel all the tasks represented by the objects in the list before you clear the list.

Upvotes: 4

Related Questions