Click here to Skip to main content
15,881,424 members
Articles / Programming Languages / C#
Article

Context Delegate

Rate me:
Please Sign up or sign in to vote.
4.96/5 (10 votes)
11 Jan 2008CPOL6 min read 41.9K   515   33   10
This is an abstraction that facilitates (but is not limited to) building multi-threaded WinForms UIs.
Image 1

Introduction

What is ContextDelegate?

ContextDelegate is an abstraction that simplifies the delivery of callbacks into any event driven, single-threaded context. A context delegate can be described as a delegate with a built-in affinity to the “context” in which it is created. When such a delegate is invoked, the target method is invoked asynchronously in the original context in which the delegate was created. When the callback-recipient context is a UI context (e.g. WinForms, WebForms), the callback is dispatched on a UI thread within event boundaries. When the callback-recipient context has no special requirements, the callback is dispatched on any available thread (possibly the calling thread).

Why is ContextDelegate Needed?

Fundamentally, a client application is an event processing engine. Application code is typically comprised of visual and non-visual objects that collectively execute in an event driven single-threaded context. In more complex client applications, the roles of the visual and non-visual objects are formalized using a Model-View-Controller design pattern.

At any moment, only one event is being processed by the client application. Most Frameworks deliver system level asynchronous activity (e.g. key press, mouse click or move events) as simple events that are serially processed by the client application. No user interface object ever needs to allow for the possibility of multiple system events racing with each other on multiple threads.

With the advent of multi-threaded client applications, it becomes necessary for the application to deal with non-system asynchronous events (e.g. arrival of data from a server, completion of a long calculation). Historically, it has been left to the application developer to arrange for these events to be safely processed by the same context (i.e. thread) that processes system events. Most resulting ad-hoc solutions involved writing a complex and error-prone mix of thread-safe and single-threaded logic. This in turn, ultimately destroyed the original simplicity and predictability of the event-driven, single-threaded UI model.

ContextDelegate provides a transparent way to deliver any application generated asynchronous event with the same contract as system level events. This allows these events to be handled with the same simplicity as system level events (e.g. keyboard clicks).

What is a Practical Example?

A typical pattern for publish/subscribe client applications involves a set of one or more threads that subscribe to published updates from a publisher and invoke callbacks for processing of those updates. The callbacks must be executed on the client (i.e. UI) thread. The callbacks must be delivered in a manner that maintains responsiveness of the user interface, even when the frequency of updates is high. ContextDelegate handles the necessary plumbing to make this possible with a minimum of effort by the application developer.

ContextDelegate Usage

While there are several types involved in the facility, only the ContextDelegate class is used by the client. The multithreaded service is unaware that it is dealing with a context-sensitive callback. A typical usage idiom involves the following events.

Image 2
  1. The client code creates a “regular” .NET delegate.
  2. The client code calls the static ContextDelegate.Create() method to create a context-sensitive delegate from the regular .NET delegate that is passed as the only parameter. A Delegate type is returned from the Create() method. The returned delegate refers to the original delegate and implies a strong reference to the target object.
  3. The client code “passes” the returned “context-sensitive” delegate returned from the Create() method to a multithreaded service.
  4. The multithreaded service invokes the “context-sensitive” delegate.
  5. The context-sensitive delegate invokes the original delegate in the context specified by the callback creator.

If the target method for the delegate is decorated with the [ThreadNeutral] attribute, the ContextDelegate.Create() method does not perform any special marshalling. This makes it possible for the ContextDelegate facility to transparently support context-sensitive and context-insensitive observers.

ContextDelegate Implementation

There are classes, interfaces, structs, and attributes involved in the ContextDelegate facility.

Name Type Purpose
AtomicCount struct Multithread-safe counter used by the callback scheduler to maintain statistics about the number of pending callbacks for a specific context.
Context class The Context for a callback consists of a scheduler, a queue of pending callbacks, and a map of delegate facades.
ContextDelegate class The public class used by the client code to create a context-sensitive delegate.
DelegateFactory class Default Factory which recreates a .NET delegate with no facade.
Facade class Provides a facade for a .NET delegate that supports context-sensitive dispatch.
Factory class Used to recreate context delegates. Advanced functionality not used by typical application code.
IDelegateFactory interface Interface used to recreate a context delegate from the underlying method and target object. Advanced functionality not used by typical application code.
IDelegateProxy interface Interface used to access the underlying method or target object. Advanced functionality not used by typical application code.
IScheduler interface The interface implemented by context-sensitive delegate schedulers.
MessageWnd class A non-display window used to schedule callbacks by the STAScheduler.
STAScheduler class A scheduler for standard apartment model threads (i.e. WinForms client code). This scheduler ensures that callbacks are delivered in a manner that ensures user interface responsiveness.
WebScheduler class A scheduler for ASP.NET client applications.
ThreadNeutral attribute Methods with this attribute will have context-sensitive delegates invoked on any available thread including the calling thread.
User32 class Singleton class that exposes selected Win32 API calls used in message dispatch.
WeakMap class A map that maintains only weak references to keys and values. WeakMap is used by the Context class to hold facades.

The ContextDelegate facility currently supports .NET delegates with the following parameter signatures:

  • (object sender, EventArgs args)
  • (IAsyncResult result)
  • (object returnValue)
  • ()

The Facade class can be extended to support other signatures. The facility currently supports a scheduler for WinForms and ASP.NET applications.

The classes in the ContextDelegate facility interact in the following manner.

Image 3

ContextDelegate

Event
1Creates a Context with an associated scheduler and dispatch queue.
2Calls the CreateDelegate method of Context to create a Facade for the requested delegate signature.

Context

Event
3Creates a Facade for the requested delegate signature.
5Queues pending callbacks for the associated scheduler. Calls the Schedule method of the associated scheduler to schedule the dispatch of the callback.

Facade

Event
4Calls the Post method of Context to schedule a callback.

STAScheduler

Event
6Calls the Post method of the MessageWnd to post a WM_USER message.
7Supplies the message procedure to handle the message posted to the MessageWnd. This message procedure is called in the context (i.e. thread) of the client that created the scheduler.
8Calls the Dispatch method of Context to actually invoke the callback.

Attachments

Visual Studio 2005 projects for the ContextDelegate implementation and a sample WinForms client application are provided in ZIP file attachments. Use the CtxDelegateSample.sln to build and execute ContextDelegate and the sample.

The ContextDelegate implementation runs in production with high update rate multithreaded services. Nevertheless, there are some aspects of the implementation that can be enhanced or further generalized. These are appropriately marked in the source code.

The sample application displays updates from a background subscriber thread on a listbox. The background thread is unaware that it is calling a method that requires UI context. The delegate used by the background thread is passed, along with update rate and update count. The callback from the thread procedure is marshaled to the UI thread by ContextDelegate. The updates are displayed in the UI thread and the user interface remains completely responsive even with high update rates.

Future Articles

In future articles, more advanced usage of ContextDelegate by infrastructure services will be explored. An ASP.NET sample application will be provided. Solutions for high update rate environments (e.g. real-time market data), within an MVC pattern, will be described.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralGood work Pin
Radim Tylecek31-Jul-08 0:30
Radim Tylecek31-Jul-08 0:30 
GeneralRe: Good work Pin
Kalkunte31-Jul-08 9:12
Kalkunte31-Jul-08 9:12 
GeneralRe: Good work Pin
Radim Tylecek31-Jul-08 20:30
Radim Tylecek31-Jul-08 20:30 
GeneralRe: Good work Pin
Kalkunte2-Aug-08 9:09
Kalkunte2-Aug-08 9:09 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.