RSS - Ian's Blog RSS - All content RSS - The Media Center Show Subscribe via iTunes  Subscribe with Zune Twitter Bookmark and Share
mControl for Windows Media Center

Multithreading in MCML

There are times when the response from a click is going to take a little while - fetching information from a web page, downloading a file, queuing a video...some of these things take time, and the user doesn't like to have their Media Center 'lock up' while they are waiting for it.

When this happens, it's time to bring out the good old Thread class once again, and start playing.

There are a couple of things you should be aware of though before you write your threaded application.

 

If you have some experience with Windows Forms applications (and I hope you do - it will make your Media Center development life much easier), you will know that it is impossible to change the text or the appearance of a control from any thread other than the thread the window was opened in (eg. the Application Thread).

The same rule applies to Media Center applications and the FirePropertyChanged function of ModelItem. If you are going to call FirePropertyChanged, it must be from the application thread, otherwise it will fail.

Note that if you call FirePropertyChanged from the wrong thread, you probably won't get an exception like you do with Windows Forms - the notification will be 'lost' and you may start getting erratic behaviour out of your application.

So how do you manage to change a property value then? Well, you create a member function that takes a single parameter of type 'object' (even if you don't USE the parameter, create it) and then call...

 

Application.DeferredInvoke(<function name>,<parameter>);

 

The 'Application' object is a global one, and the DeferredInvoke function tells Media Center and the .NET system to run the <function name> function in the APPLICATION's thread, next time that thread is available for a little extra work.

I personally like to make my work even easier, by using the following code in any property that I expect to be accessed from a thread other than the main application thread...

In The Object That Has Properties....

internal void FPC(object Val)

{

    String Value = (String)Val;

     if (Application.IsApplicationThread() == true)

         FirePropertyChanged(Value);

    else

    {

         Application.DeferredInvoke(FPC,Value);

    }

}

 

The Actual Property Itself... 

string _MyStringValue;

public string MyStringValue

{

      get { return _MyStringValue; }

      set

      {

            _MyStringValue = value;

            FPC("MyStringValue");

      }

};

 

Because the FPC (which is short for FirePropertyChanged) actually conforms to the format needed to pass to the DeferredInvoke function, it's actually recursive. If you try to set the MyStringValue property, FPC is called.

If FPC detects that it has been called from the application thread (by using the IsApplicationThread() command), it simply calls FirePropertyChanged. If it finds that it isn't in the application's thread, it calls itself again using DeferredInvoke. The 2nd time it runs, it WILL be in the right thread.

This greatly simplifies your programming, because you simply have to make sure that you ALWAYS use the publically exposed property name when you are accessing MyStringValue.

 

Unfortunately, while this protects you when playing with your own properties that are based on simple type (eg. Int32, String, Bool etc.) it won't help with any type that is already based on ModelItem.

These types - like IntRangedValue, Choice, EditableText, ArrayListDataSet etc. already use FirePropertyChanged internally. So you must ensure that you only actually interact with these classes in the correct Application thread.


Posted Dec 10 2007, 09:32 AM by IgnoranceIsBliss

Comments

impf wrote re: Multithreading in MCML
on 12-18-2007 11:47 AM

In my application I periodically read some data from an external device an show them within Media Center (IntRangedValue, BooleanChoice, Choice, EditableText...)

The user may change some of these values so I've to write those changes back to the external device. I've done this without threading - and therefore some changes the user made or some values from the device get lost!

Do I have to start 2 threads in my application class in which the first one reads the value and updates the view while the second one waits for user interaction e.g. click-events to write those values back to the device? Could you show me an example of a worker thread generating/displaying vlaues while another thread handle unser interaction, lets say increment/decrement those values....

or is there even a better approach??? thanks

IgnoranceIsBliss wrote re: Multithreading in MCML
on 12-18-2007 7:41 PM

To cover the basics, no - you do not have to create two threads, since your main application is already on it's OWN thread (every application must have at least one).

So you only need to create a single thread - a WORKER thread - to do all of the hard work of sending data to and from your device. The main thread, or 'application' thread (the one that was created when your program started up, and handles all of the MCML interaction) will keep running and doing everything it's supposed to do in the background.

I'm afraid I'm very busy at the moment, so I can't show you a complete example. But as I said in the first posts of this blog, you really should have some experience writing Windows Forms applications before tackling MCML, which should have given you experience with multithreading. So you will find MANY examples, in both the .NET SDK and across the internet in general, of multi-threading in C#. But at a very basic example...

You create a function in your class that contains all of the work your thread is going to do (you can of course call other functions FROM this one). For example, "GenerateValues", in your request above.

In your main class (the one that does the bulk of the interaction with MCML), you declare a thread variable...

System.Threading.Thread MyThread;

In your class constructor, you start the thread...

MyThread = new System.Threading.Thread(new System.Threading.ThreadStart(<my function name>));

MyThread.Start();

This then starts the <my function name> function...but rather than waiting for it to finish - which is what happens when you normally run a function - it will instead run it on a brand new thread, so your code will immediately continue.

For more details, please search Google for multithreading in C#. I've covered the only real difference between multithreading in Forms and multithreading in MCML in the post above.

impf wrote re: Multithreading in MCML
on 12-20-2007 8:43 PM

I found this... http://www.albahari.com/threading/part3.html#_BackgroundWorker I'll do the data read/write in the bw_DoWork method where Thread.Sleep (1000); could slow down as appropriate. The main thread which will do the user interaction and FirePropertyChanged() is my main application class as you mentioned above.

Do you think that's the way to go?

IgnoranceIsBliss wrote re: Multithreading in MCML
on 12-21-2007 6:09 AM

I don't know if you can use can use BackgroundWorker in an MCML, but it's worth a try. I personally just use the Thread class, but that's because I'm very used to working with them.

But yes, that's the basic idea. You launch a thread, which then does all the work in the 'background' while you do everything else. Just remember to tell MCML when you've update the data, either by a 'callback' to the main thread ("I'm Done" event handler, for example) or by calling FirePropertyChanged on the ModelItem based classes as you update them - of course, following the delegating methods I've outlined above.

There is also the Application.DeferredInvokeOnWorkerThread - this is just like Application.DeferredInvoke, but it also calls a function on the APPLICATION thread when it's done. So if you updated data, but didn't need to show anything until you were done, you could use the DeferredInvokeOnWorkerThread function to physically update the data, but call all of your FirePropertyChanged events in the SECOND function.

Add a Comment

(required)  
(optional)
(required)  
Remember Me?
(c) Ian Dixon 2008