DCSIMG
Dependency property getters and setters in multithreaded environment - Just code - Tamir Khason
Sunday, April 29, 2007 7:54 PM Tamir Khason

Dependency property getters and setters in multithreaded environment

[This blog was migrated. You will not be able to comment here.
The new URL of this post is http://khason.net/blog/dependency-property-getters-and-setters-in-multithreaded-environment/]


Recently I blogged about Thread safe ObservableCollection. And what's about regular single Dependency Properties? Actually, the story is rather same and the rule is clear - Bring into your STAThread first, then do whatever you want to do. So how to do it? Simple Let's create regular window with two DPs MyTextProperty and ButtonTitleProperty. We'll bind TextBox to MyTextProperty and will change it from other thread, based on ButtonTitleProperty value.

image

So, first of all those properties

        public static readonly DependencyProperty ButtonTitleProperty = DependencyProperty.Register("ButtonTitle", typeof(ButtonStates), typeof(Window1), new UIPropertyMetadata(ButtonStates.Start));
        public static readonly DependencyProperty MyTextProperty = DependencyProperty.Register("MyText", typeof(string), typeof(Window1));

Now, the crazy thread

Random r;
        void updateText()
        {
            r = new Random();
            while (ButtonTitle == ButtonStates.Stop)
            {
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < 10; i++)
                {
                    sb.Append((char)r.Next(0x41, 0x51));
                }

                MyText = sb.ToString();

            }
        }

 

And finally the method to invoke it

void onClick(object sender, RoutedEventArgs e)
        {
            if (ButtonTitle == ButtonStates.Start)
            {
                //start
                new Thread(new ThreadStart(updateText)).Start();
                ButtonTitle = ButtonStates.Stop;
            }
            else
            { 
                //end
                ButtonTitle = ButtonStates.Start;
            }
        }

<Button Click="onClick" Content="{Binding ElementName=myWindow, Path=ButtonTitle}"/>
<TextBlock Text="{Binding ElementName=myWindow, Path=MyText}"/>

 

 

Now, try to run - the first thing will happen is exception: The calling thread cannot access this object because a different thread owns it. in ButtonTitle getter The next - the same exception in MyText setter. What to do? Bring the value into STAThread. How? Simple instead of regular getter

 get { return (ButtonStates)GetValue(ButtonTitleProperty); }

 

We'll use thread safe getter

get
            {

                try
                {
                    return (ButtonStates)this.Dispatcher.Invoke(
                       System.Windows.Threading.DispatcherPriority.Background,
                       (DispatcherOperationCallback)delegate { return GetValue(ButtonTitleProperty); },
                       ButtonTitleProperty);
                }
                catch
                {
                    
                    return (ButtonStates)ButtonTitleProperty.DefaultMetadata.DefaultValue;
                }
            }

 

And instead of regular setter

set { SetValue(MyTextProperty, value); }

 

We'll use thread safe setter

set
            {
                this.Dispatcher.BeginInvoke(DispatcherPriority.Background,
                    (SendOrPostCallback)delegate { SetValue(MyTextProperty, value); },
                    value);
            }

 

I believe it's clear why getter do synchronous and setter does not? :)

Try to experiment with DispatcherPriority enum to see how busy your system will be. Good luck

Source code for this article

תגים:, , ,

Comments

No Comments