As promised, I have some ideas regarding Async code for beginners. Part one of three.
Compact Framework
The first thing to learn when doing asynchronous programming on the Compact Framework is that it is not as forgiving as the full .NET framework. Threading things that might be a problem on a Pentium IV will be a problem on a 400mhz XScale. The first problem you may run into, since its not well documented is Thread priorities. You may want to give your "worker" threads more CPU time than the UI thread. While the thread priority method exists on the thread class in the CF it is unofficially unsupported in CF 1.0. I have tested this on at least 3 different devices with both CE.NET 4.2 and Windows Mobile and any attempt to set a thread priority locks up execution and debugging.
Your UI is running on a thread that processes events and updates UI controls. In fact the compact framework starts quite a few threads you have no control over, for exampleTCP listeners and such when you execte web services. Any attempt to access a GUI control's properties from outside this UI thread could cause unexpected behavior, including the program locking up and no longer responding. Often you can get away with reading a control's properties without locking up, but setting a Label Text property is much more likely to cause a deadlock. The way you access a control's properties from your worker threads, then, is using Control.Invoke. The method description for Control.Invoke says:
"Executes the specified delegate on the thread that owns the control's underlying window handle."
That should be fairly straight forward. There is a Message Pump running. Every time something happens in GUI code that changes some visual aspect of a control it occurs as the Message Pump dispatches messages. Code you call directly from the UI thread automatically gets executed this way, Control.Invoke gives you a way to execute some code from within this message pump without exposing the internals to you. On Compact Framework 1.0 only the version of Control.Invoke that takes a no-arguments Delegate is supported. In CF 2.0, Control.Invoke (Delegate, object[]) is also supported, which eliminates the need for the following tip:
Since you cannot pass arguments to Control.Invoke in CF 1.0 you may need a way to communicate with your UI controls from a worker thread. For example, suppose you have a custom sync process running on a worker thread and you want to update a Label each time a table is processed with the name of the next table name. I would argue that the worker thread having any knowledge of the UI controls is bad, so you need a better means of communicating. The Mediator/Director pattern from the GOF design patterns book provides a solution. Consider the following:
On the Worker thread class:
On the Director we have a method or property we can talk to which keeps the UI control decoupled from other classes:
...public string SyncMessage{get{ return _syncMessage;}set{_syncMessage = value;_someControl.Invoke( new EventHandhler(_someControl.SyncMessageChanged));}}...
On the GUI control owning the Label, which also kicked off the worker thread, you supply an EventHandler:
public void SyncMessageChanged(object sender, EventArgs e){ _someLabel.Text = Director.Instance.SyncMessage;}
Note that in some cases you may wish to synchronize access to the value from the Director. Every application is different. This is a somewhat simplified example. In my real applications I typically have a "Context" (Possibly State pattern) object of some kind which keeps State for me, and the Director just fires messages. There are different techniques to use, but I will offer some tips:
Modal Mixing
The very excellent OpenNETCF libraries for the .Net compact framework provides some extended functionality for processing windows messages. Much of this is in the ApplicationEx class, allowing you to run your GUI's main form using ApplicationEx.Run( new MyForm() );. I have also experienced different GUI lockup situations where different ShowModal() and MessageBox calls are mixed. If you need to show a modal Form be sure to use the new ApplicationEx.ShowDialog(Form, bool dispose) rather than Form.ShowDialog(); this will ensure the form is blocking the correct message pump.
The next threading article will be a short one discussing a couple of easy performance tricks you can use Threads for in ASP.NET. In the article after that I will show some other threading techniques.