Tuesday, November 8, 2011

So that your threads do not tangle..


I had never thought an application could crash of an unsafely handled label. But this did happen with me once when this exception popped up out of nowhere and said: InvalidOperationException : Control label1 accessed from a thread other than the thread it was created on. As .Net developers, we usually assume that all such stuff is being taken care of by the framework or the visual studio. Hence this was something new for me. As I figured out, this is a compile time exception which occurs to warn the developer of a situation when more than one thread would try to access the same control and hence get locked. This exception usually occurs during compile time and reliably during debugging and sometimes during run-time.

Access to Windows form controls is not inherently thread safe and this is pretty much the reason why one has to take care of it. So here is how you go about making thread safe calls.
Spoiler for over confident people : If you’re sure there will never be a conflict between threads for the same control in your application you can directly turn this exception off by setting the CheckForIllegalCrossThreadCalls property to false.

The concept of making a thread safe call consists of 2 simple steps:
1.       Find out if the control is being accessed by a thread other than the thread which created the control.
2.       Directly access if the control is accessed by creator thread, else the accessing thread makes an asynchronous call to itself.
To find out if the control is accessed by another thread we check the InvokeRequired property of the control. This property checks the thread ID of creating thread and accessing thread and gives back a TRUE value if they are different.
So instead of directly making my label visible like
Label1.Visible = true;
I put a method of my own :
ShowLable1(true);

And the method handles the safe operation as  :
        private void ShowLable1(bool p)
        {
            if (Label1.InvokeRequired)
            {
                SetFlagCallBack fg = new SetFlagCallBack(Lable1);
                this.Invoke(fg, new object[] { p });
            }
            else
            {
                Label1.Visible = true;
            }
        }
So now my control was accessed in a thread safe way and I never got that exception.
Another way of doing this is using a Background worker. In this case, you handle the control operation using the asynchronous call of the worker i.e RunWorkerAsync(). You then set any properties of the control from the RunWorkerCompleted() handler of the background worker. This handler is called when the worker asynchronous call is complete and the control property is changed on the same thread which created the control. Thus this way is automatically thread safe and seems to be the preferred way of doing it.
One has to be careful about such things while working with multiple threads since it can give rise to complex and hard to track bugs. It would be good if one adheres to some best practices while working with multiple threads. These links can be of help in this case :

No comments: