I have been thinking about this topic over the last few days. How should you build concurrency into user interface processes. In this context concurrency is used entirely to ensure responsiveness, not due to any form of concurrent nature of the application. In other words how can we gain the required responsiveness without introducing the issues related to concurrency.
As some of the previous blogs entries on this have discussed, concurrency is difficulty, with shared data being the cause of the issues. Within the user interface only limited sharing of data between threads is only required. We need to communication progress, and completion (with success or failure etc).
While I have been thinking about this in the back of my mind, I have also been reviewing some of the material I use to teach enterprise development with .NET. In particular, User Interface Process Components, see http://msdn.microsoft.com/library/en-us/dnbda/html/AppArchCh2.asp?frame=true. I think the concept of User Interface Process Components is great. The application block, http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/uipab.asp, is ok but IMO lacks some basic functionality. Mainly I would liked to have see the ability to link actions to user interface events, rather than just transitions. Transitions can then become actions, multiple actions can be performed by composition. Having this reduces the functionality of the user interface components to notifying the user interface process of certain events + managing the view. The "What happens" part of the application is controlled by the user interface process.
Given this, it would be nice to have asynchronous support built into this. Given tasks (or parts of tasks) could then be marked as asynchronous. Running these tasks is then coordinated by the UIP framework. Asynchronous tasks could be given the ability to indicate progress. The coordination of data required to do this would also be managed by the framework. Basically, by using this developers will be able to gain responsiveness, without (as many) concerns for concurrency.
I haven't spent anywhere need long enough thinking about this but I am interested in any comments you have.
---
Issues I can think of off the top of my head:
1: Interaction with the UI. If a task is asynchronous the developer must not interact with GUI elements. Doing so is asking for disaster...
2: Web application. I think this will be ok but there are some tricks that need to be done to get this to work ok.
---
Just some random thoughts on this (so I don't forget):
UIC --> UIP (User interface components are dependent on User Interface Processes)
Each UIC would have a specific functionality, for example collecting data from the user and storing it in the UIP. Therefore UIP is not dependent on UIC.
UIP needs to move to new views. UIP --> UIC... UIP <--> UIC. However page transitions are already implemented in the application block. This interdependence is already present within the block.
Moving actions into UIP ensures that it is encoded here and not in the UIC.
Do you really want one UIP for web and windows? Does the extra complexity outweigh the benefits?
---
Any thoughts on this are very welcome.
Thursday, January 27, 2005
Asynchronous User Interfaces
Posted by Andrew Cain at 3:11 pm 0 comments
Sunday, January 23, 2005
Concurrency Articles
I have uploaded a couple of articles on concurrency. These are very introductory and cover race conditions, and locks.
Having written these I think I need a thread basics to introduce threading itself as a concept. The next article will cover liveness and deadlocks. I plan then to introduce other thread exclusion mechanisms, and then move onto coordinating multiple thread.
Any comments will be greatly appreciated.
Posted by Andrew Cain at 3:40 pm 1 comments
Friday, January 21, 2005
Concurrent Programming
As Matthew points out on his blog, another potential solution to the concurrency issue is to stop further requests by disabling the button...
I have updated by DeadLock2.zip file to include a new fourth solution.
In this solution I have separated out the logic of the library functionality. I think I understand the intended model a little better now, and as I see it, it is responsible for echoing a stored message + some additional string. I have coded this into an "Echoer" class that internally stores the message.
Using this library, the UI requests an echo from the Echoer with the additional string "1 ". The result that is echoed is then used to update the Echoer's message property.
If you examine the solution I have uploaded, you will notice that there are no locks used. This implementation is thread safe due to the structuring of the program. Updates to the Echoer's Message property occur on the GUI thread. The worker thread is responsible for processing the echo and returning the new string. The thread safety is ensured by stopping the GUI thread from interacting with this variable until after the transaction is completed.
So why the differing views?
There are two perspectives on this issue. When I read the code originally I was viewing it from a concurrency mindset. Therefore you look for potential cases of concurrency and try to determine all of the potential race conditions. The fix to this issue is then to modify the library to include thread safety mechanisms. Thereby ensuring that the library will be usable in a concurrent environment.
We can also consider this from the perspective of an application developer using a non-thread safe library object. In this case the responsibility of ensuring thread safety lies in the UI, as the library cannot be changed.
And the motto of the story is?
Writing good concurrent programs is not hard... it is very hard. A slight oversight and you will have introduced unexpected bugs. These bugs are not the friendly kind that will appear in testing, these are the nasty kind that only show up on rare occasions (in my experience this is usually when it is critical that the don't show up).
One of the most important things to consider with any of these solutions is communication. How do you communicate that the line "button1.Enabled = false;" is absolutely critical to the safety of the application? If this line is removed, the safety of the application is compromised. In a team development environment you need to ensure that there is a good understanding of the strategies used to ensure that the application remains safe and live.
Patterns can help greatly with communication. I am really looking forward to seeing Matthew's future entries on this topic...
On Invoke and BeginInvoke
Basically I agree with Matthew, BeginInvoke is usually the better option. However, I would not go as far as saying that Invoke should never be used. Rather, it is my opinion that you should understand what you are trying to do and choose the one that is most appropriate. This in most cases will be BeginInvoke.
Posted by Andrew Cain at 8:11 am 0 comments
Thursday, January 20, 2005
Beware BeginInvoke
Updated 21/01: Included additional solution in download. See this entry.
After reading Matthew's blog entry in detail I have a few comments...
The code for this is available for download.
Is this really thread safe?
To answer this we must ask, what is the intended semantics? While there is little to go on, I have assumed that each click of the button should add the text, "plus a bit" to the end of the label. So clicking this button multiple times should result in that number of "plus a bit"s being added to the label.
Does this occur? What does the 'evil little daemon' have to say about this?
Download the code and give it a try... Click the button quickly 10 times, and watch the result.
It works... But is it safe?
The problem is that the evil little daemon is a slacker! He (or she) does not want to work all the time. So lets give him a hand.
Try inserting the line "System.Threading.Thread.Sleep(1000);" after the lock.
This puts the current thread (i.e. the worker) to sleep for 1000 milliseconds, giving the evil little daemon a chance to work his magic.
Run it again and see the results, clicking the button 10 times. In my case three "plus a bit"s were added to the label, with a little delay due to the sleep. Why? Race conditions, as I discussed earlier.
To view the problem for yourself, download the new version of the code and run it in the debugger. You can then view the changes that occur on the instance variable in the output window. You should notice that the value gets updated the correct number of times, but that the values are wrong (much like the account problem I discussed earlier).
Where is the problem? The problem exists because of the locking strategy. In this case the lock is acquired to read the value, but is then released allowing other threads to read and write to this value. This thread plans on reading the value, changing the value, and writing the value back (via the GUI thread). Because the lock is released after reading, it is possible that it has changed before the write occurs. As the semantics of our program require that none of these values get lost, this is not safe. So what can be done?
There are several potential approaches, so lets discuss them.
Solution 1
If the update can be performed on the worker thread then the update is simple. Move the updating of the value into the lock with the read. Now that is code is updating the instance variable we do not need to tell the GUI of the value of the string, the GUI can re-query for it. As a result we can use a standard method invoker and get the form to update itself.
The new code looks like this (I have changed "plus a bit" to " 1" as it is easier to count the correct number in the output)
lock( littleLockObject )
{
//read and update the string...
myUsefulString += " 1";
}
To make the test fairer this can be changed to:
lock( littleLockObject )
{
string copy;
copy = myUsefulString;
System.Threading.Thread.Sleep(1000);
myUsefulString = copy + " 1";
}
This way the sleep is between the read and the write, as it was before.
With this we can now consider the String to be our model and GUI our view. The GUI code only views the value, it doesn't update it. Ideally we could move this into another class, and then raise an even upon change etc...
When you run this you will see that all of the clicks get through to the label.
Solution 2
A much more complex solution is to remain with the updating of the instance variable from the GUI thread. In order to ensure that this works safely we must ensure that only one thread can be performing this operation at a time. We can do this by introducing a boolean variable that controls access to this functionality. The body of the method can then be guarded via a condition on this variable, see the code below:
public class Form1 : System.Windows.Forms.Form
{
private bool _MutexAvailable = true;
private void DoSomeWork()
{
string copy;
lock( littleLockObject )
{
while(false == _MutexAvailable)
Monitor.Wait( littleLockObject );
_MutexAvailable = false;
copy = myUsefulString;
}
So how does this work? The thread waits until _MutexAvailable is true, it then sets _MutexAvailable to be false and then processes the body of the method. No others can continue as they are blocked by the guard, i.e. they are waiting for _MutexAvailable to become true.
At the end of the method we now need to reset _MutexAvailable and wake any waiting threads. This must be done in a critical section to ensure safety. So the following code is added to the end of the method:
lock( littleLockObject )
{
_MutexAvailable = true;
Monitor.PulseAll( littleLockObject );
}
The PulseAll wakes the threads from the Wait at the top of the method.
Run the program again. Leave in the Sleep so that we are testing the same thing. Now all of the clicks result in an update of the label.
Is this safe? I am afraid that it is not...
Why?
Try adding a sleep to the UpdateTheUI method.
Running the program now causes incorrect updating again. The problem is now the use of BeginInvoke. With BeginInvoke a new thread is being used to update the value. This allows the worker to pulse another thread and have it read a stale value from the instance variable. The solution is to replace BeginInvoke with Invoke. It is critical that we wait at this point until after the instance variable is updated.
Running this again, with the two sleeps, and now it works correctly.
Solution 3
The problem with solution 2 is that it is full of thread management code that is not fun to play with (for most people). This can be extracted out into a utility class and can then be reused again and again. In this case the utility is called a Mutex. However, this is not the Mutex that comes with .NET, rather it is a lightweight Mutex.
The code now looks like this:
private void DoSomeWork()
{
string copy;
_MyMutex.Acquire();
try
{
//no need for lock, this is a reference to a immutable object
copy = myUsefulString;
System.Threading.Thread.Sleep(1000);
if( this.InvokeRequired )
{
Invoke( new StringMethodInvoker( UpdateTheUI ), new object[] { copy + " 1" } );
}
else
{
UpdateTheUI( copy + " plus a bit" );
}
}
finally
{
_MyMutex.Release();
}
}
The lock is no longer used as the Mutex provides us with the concurrency control.
Conclusion
Concurrency is difficult. There are no magic solution. BeginInvoke is a great tool, but you must take care if you want to ensure total safety.
Having said this, I cannot imagine the circumstances that would be required for this safety issue to raise itself without the inserted Sleep statements. Personally, however, I think you are best to try to make your application as safe as possible (i.e. live + safe).
Posted by Andrew Cain at 2:21 pm 0 comments
Interacting with a GUI
If you look at GUI libraries in the .NET and Java platforms, you will find that these both require that interactions with the GUI are performed upon a single thread. This has become a hot topic lately, see Matthew Adams blog, and the related links.
So why is this implemented in this way?
This is a form of exclusion, enforced only by documentation (and the fact that you risk deadlocks if you ignore this).
Exclusion?
In a concurrent program multiple threads may attempt to access data elements at the same time. This can lead to a number of issues, referred to as race conditions. For example.
Consider the following pseudocode:
Thread 1:
- Read account value
- Increase value by $100
- Write back new account value
- Read account value
- Increase value by $50
- Write back new account value
Why?
The execution may occur in such as way that the result, after completion, does not result in the balance of the account increasing by $150. Imagine the following execution:
- Thread 1: Reads the account value as $1000 (the starting balance)
- Thread 1: Adds $100 to this, the result being $1100 is currently stored in a temporary variable.
- Thread 2: Reads the account value as $1000 ($1100 has not been stored yet!)
- Thread 2: Adds $50 to this, the result being stored in a temporary variable.
- Thread 2: Writes the value back into the account, the accounts balance is now $1050
- Thread 1: Writes the value stored in its temporary variable back into the account. The accounts balance is now $1100!
The issue then is, so how do you interact with the GUI? The answer is, with care and invoke. I will leave the details for you to read in the linked articles above.
Posted by Andrew Cain at 1:33 pm 0 comments
Wednesday, January 19, 2005
Concurrency
Yesterday I was sent an interesting article on concurrency. I have had a quick skim read of it and it is basically echoing what I have been saying over the last few months... Concurrency is the next big step in computing. Why? Multi-core CPUs.
In order to take advantage of multi-core CPUs, applications will need to be developed that use multiple threads. Multiple threads = concurrency. Concurrency = "You need to know what you are doing!" (have a read of some of the links from my site to concurrency issues such as Double Check Locking).
Having read this, it leads me to thinking, can you introduce concurrency into applications? I would agree to some degree that you can... You can offload things like loading images, saving and loading files, and background tasks., but generally the core functionality is usually single threaded. Will the offloading of "background" tasks give sufficient performance advantages, given the addition of concurrency control mechanisms? This is something that I need to do some more thinking on and will post my thoughts...
Let us consider an application example: PowerPoint: what threads could exist in this application?
- Load and render slides (potentially many threads, using structural exclusion to load different slides)
- User interface management (the GUI thread, interacted with from the threads above)
- Save thread (save files... requires lock on model...)
- Spelling and Gramma thread (interacts with changing model, and UI)
So multiple threads is possible, but this has greatly increased the complexity of the application. The model now requires appropriate critical sections, and/or locking strategies. How would this work in a team environment? Communication is the key. More developers need to know and understand concurrency, and strategies to overcome concurrency issues.
Beware Shameless Plug Below
So this bring me to the question:
Why do Advanced .NET at Swinburne?
Until recently Advanced .NET has been a subject that covered topics that most people would never work on again in their lives. This subject is hard. Most people work really really hard, and still only get a passing mark... What is the deal?
Advanced.NET deals with concurrency. This subject will help you learn about developing safe and effective concurrent applications.
Posted by Andrew Cain at 7:19 am 0 comments
Tuesday, January 18, 2005
Welcome Message
Welcome to my new BLOG.
My previous blog on the byte club server will no longer be active... Please ensure that you use my new feed to keep up to date.
If you are interested in finding older entries please refer to my old blog.
This blog will discuss my thoughts on software development, teaching, and other (sometimes related) work.
Posted by Andrew Cain at 3:27 pm 3 comments