Disposing an object from an internal thread.

Napivo1972

Regular
Joined
Aug 31, 2004
Messages
85
I suck at threading but now I really need it...

I have an VideoTexture class that uses an internal thread to monitor the events of the video. when the video is done it raises an event complete.

Now the event is captured in my XNA game class and it loads another video (Actually switches to another one that was loaded in the background)

Since the videoTexture is pretty heavy on resources I need to dispose of it. I can't call a thread abort since that would kill my thread I use to make the switch.

How do I solve this?
 
I think that the best thing to do would be to raise an event on the main thread or otherwise notify the main thread when the video is done. The main thread can get the second video going and let the VideoTexture object's thread complete gracefully instead of aborting the thread, and then dispose it.
 
That's sound really nice but how can I invoke the event on the main thread in a fully isolated class?

I have no reference to my main form (which is an XNA Game window) and would like to keep it that way, if at all possible.
 
I'm not sure whether you mean that you don't want your VideoTexture to reference the main form, or that you don't want your game class to reference the main form. It shouldn't matter, though. You can have your Game class queue code or tasks to run on your main thread, and have the Update routine perform these tasks. Since the Update method is run on the main thread, the tasks you queue will be run on the main thread.

Code:
class AGameClass {
[COLOR="Green"]    // Used to lock syncronous operations[/COLOR]
    object _taskQueueLock = new object();
[COLOR="Green"]    // Our queue of tasks[/COLOR]
    Public List<Action> taskQueue = new List<Action>();

[COLOR="Green"]    // Can be called from any thread[/COLOR]
    public void QueueAction(Action action) {
        lock(_taskQueueLock) {
            _taskQueue.Add(action);
        }
    }

[COLOR="Green"]    // Will be called from main thread[/COLOR]
    protected override void Update(Some parameters) {
        RunQueuedTasks();    
[COLOR="Green"]        // Other stuff[/COLOR]
    }

[COLOR="Green"]    // Will run queued tasks on main thread and block threads that try to queue more tasks in the interim
    // Watch for race conditions! If you want a queued task to be able to enqueue more tasks, you'll need
    // a better approach for managing your task queue (a thread-safe list class should work).[/COLOR]
    private void RunQueuedTasks() {
        lock(_taskQueueLock) {
            foreach(var action in _taskQueue) {
                action();
            }
        
            _taskQueue.Clear();
        }
    }
}

class AnotherClass {
    protected override void SomethingHappened() {
        aGameObjectThanImReferencing.QueueAction( new delegate() {
[COLOR="Green"]            // Dispose method can free unmanaged resources, including
            // threads and graphic resources
[/COLOR]            SetUpAnotherVideo();
            this.Dispose();
        });
    }
}


Understand, I'm not a threading expert. That, and this is sort of off the top of my head, but it seems about right to me... :>
 
That could work searching looking around the Internet a bit more I found this. At least you thought me something. Thank you.

http://www.codeproject.com/KB/threads/CrossThreadEvents.aspx

I tried converting it to c# but for some reason this throws a compiler error.
Code:
private void InvokeAction<T>(EventHandler<T> anAction, T arg, bool ThrowMainFormMissingError = true)

So in the end I came up with this far less generic function but it seems to do the job...

Code:
private void InvokeAction(EventHandler<EventArgs> anAction, EventArgs arg, bool ThrowMainFormMissingError = true)
        {
            if ((!ThrowMainFormMissingError) && (Application.OpenForms.Count == 0))
            {
                return;
            }

            try
            {
                Form f = Application.OpenForms[0];

                if (f.InvokeRequired)
                    f.Invoke(anAction, this, arg);
                else
                    anAction(this, arg);
            }
            finally
            {
            }
        }

What I really meant was, the XnaForm and game class are actually the same class but My Video Player Class is unaware of either. It just loads the frames and prepares a Texture2D (Comparable to a bitmap) actulaly the Class Name is VideoTexture2D.

I was trying to keep it that way so I didn't have to pass around a lot of references and that it would work even if there is no Game class... for instance, if I just want to grab frames and save them to file.
 
I was trying to keep it that way so I didn't have to pass around a lot of references and that it would work even if there is no Game class... for instance, if I just want to grab frames and save them to file.
Understandable, but a very simple change could help you avoid directly referencing your game object.
Code:
class AGameClass {
    // Used to lock syncronous operations
    object _taskQueueLock = new object();
    // Our queue of tasks
    Public List<Action> taskQueue = new List<Action>();

    // Can be called from any thread
    public void QueueAction(Action action) {
        lock(_taskQueueLock) {
            _taskQueue.Add(action);
        }
    }

    // Will be called from main thread
    protected override void Update(Some parameters) {
        RunQueuedTasks();    
        // Other stuff
    }

    // Will run queued tasks on main thread and block threads that try to queue more tasks in the interim
    // Watch for race conditions! If you want a queued task to be able to enqueue more tasks, you'll need
    // a better approach for managing your task queue (a thread-safe list class should work).
    private void RunQueuedTasks() {
        lock(_taskQueueLock) {
            foreach(var action in _taskQueue) {
                action();
            }
        
            _taskQueue.Clear();
        }
    }
            
 [COLOR="red"]   // Assuming _videoTexture_1 has already been instantiated and wired for events
    AnotherClass _videoTexture_1;
    AnotherClass _videoTexture_2;
[/COLOR]
[COLOR="red"]    _videoTexture_1_AThingHappened(object sender, EventArgs e) {
        // Dispose the first video texture and create the second
        QueueTask(new delgate() {
            _videoTexture_1.Dispose();
            
            CreateAndInitialize_videoTexture_2();
        });
    }[/COLOR]
}

class AnotherClass {
[COLOR="red"]    public event EventHandler AThingHappened;
[/COLOR]    
    protected override void SomethingHappened() {
[COLOR="red"]        // Raise an event when appropriate
        var tmpAThingHappened = AThingHappened;
        if(tmpAThingHappened != null)
            tmpAThingHappened(this, EventArgs.Empty);
[/COLOR]    }
}
The effect of the code is the same, but the game creates each video texture instead of one creating another. It makes more sense to have the game class managing these things (or a Scene or Sequence or Montage or whatever class if you're working on a larger, more complex project) than to have the video texture doing more than being a video texture.
 
Back
Top