Showing posts with label Visual Studio 2010. Show all posts
Showing posts with label Visual Studio 2010. Show all posts

Tuesday, January 4, 2011

.NET 4 Concurrent Dictionary Collection

In ASP.NET we use a few static generic dictionaries that store data which needs to be accessed very quickly. Generic dictionaries are not thread-safe when adding items so we had to provide locks around all access to the collection.

Example using locks to add thread-safety when manipulating a generic dictionary.
private static readonly object cachedAnonymousIdentitiesLock = new object();
private static readonly Dictionary<Guid, Identity> cachedAnonymousIdentities = 
    new Dictionary<Guid, Identity>();

private Identity GetAnonymousIdentity(Guid orgId)
{
    Identity id = null;
    lock (cachedAnonymousIdentitiesLock)
    {
        if (cachedAnonymousIdentities.ContainsKey(orgId))
            id = cachedAnonymousIdentities[orgId];
        else
        {
            id = new Identity(orgId);
            cachedAnonymousIdentities.Add(orgId, id);
        }
    }
    return id;
}

The code above uses a mutual exclusion lock to provide thread-safe access to the generic dictionary which contains an immutable Identity object. While this code works fine, it does take a performance hit because of the blocking mutual exclusion lock. If a thread becomes blocked because of the lock, extra overhead is encountered because a context switch in the OS will occur. We really only need to maintain a lock for a very small amount of time while a new item is added to the collection (and truthfully a spin lock would be more efficient here).

Now in case you haven't heard, there is a new namespace in .NET 4 for concurrent collections. When I found out about this I immediately got the refactor itch. The thread-safe collections are discussed on MSDN. Looking around I found the System.Collections.Concurrent.ConcurrentDictionary<TKey, TValue> class. This class uses a spin lock instead of a blocking lock for thread-safe access. Additionally the class provides a few friendly methods that make adding a new item to the dictionary easier.  The GetOrAdd method is a welcomed method that allows you to quickly either get the item or add it to the dictionary.  Below is the same code above refactored using the ConcurrentDictionary.

private static readonly ConcurrentDictionary<Guid, Identity> cachedAnonymousIdentities =
    new ConcurrentDictionary<Guid, Identity>();

private Identity GetAnonymousIdentity(Guid orgId)
{
    return cachedAnonymousIdentities.GetOrAdd(orgId, key => new Identity(orgId));
}
The code is easy to follow and the locking is done automatically! I ran a quick unit test to double check the efficiency of the ConcurrentDictionary over our previous blocking method. The results show that in our scenario we do achieve a performance boost. Running the example below on a multi-core machine results in the blocking example executing in about 450 milliseconds while the concurrent example executes in about 350 milliseconds.

private readonly static ConcurrentDictionary<Guid, string> myThreadsafeObjects = new ConcurrentDictionary<Guid, string>();
private readonly static Dictionary<Guid, string> myObjects = new Dictionary<Guid, string>();
private readonly static object lockObj = new object();

[TestMethod]
public void LockingTest()
{
    int iterations = 1000000;
    string value = "hello world";
    int keyCount = 10000;
    List<Guid> keys = new List<Guid>(keyCount);
    for (int i = 0; i < keyCount; i++)
        keys.Add(Guid.NewGuid());
   
    Stopwatch lockSw = new Stopwatch();
    lockSw.Start();
    Thread t1 = new Thread(() =>
    {
        for (int i = 0; i < iterations; i++)
        {
            lock (lockObj)
            {
                if (!myObjects.ContainsKey(keys[i % keyCount]))
                    myObjects.Add(keys[i % keyCount], value);
                string itemValue = myObjects[keys[i % keyCount]];
            }
        }
    });
    t1.Start();
    for (int i = 0; i < iterations; i++)
    {
        lock (lockObj)
        {
            if (!myObjects.ContainsKey(keys[i % keyCount]))
                myObjects.Add(keys[i % keyCount], value);
            string itemValue = myObjects[keys[i % keyCount]];
        }
    }
    t1.Join();
    lockSw.Stop();
    Trace.WriteLine(string.Format("Blocking Dictionary lock test: {0} milliseconds", lockSw.ElapsedMilliseconds));

    Stopwatch concurrentSw = new Stopwatch();
    concurrentSw.Start();
    Thread t2 = new Thread(() =>
    {
        for (int i = 0; i < iterations; i++)
        {
            lock (lockObj)
            {
                string itemValue = myThreadsafeObjects.GetOrAdd(keys[i % keyCount], value);
            }
        }
    });
    t2.Start();
    for (int i = 0; i < iterations; i++)
    {
        lock (lockObj)
        {
            string itemValue = myThreadsafeObjects.GetOrAdd(keys[i % keyCount], value);
        }
    }
    t2.Join();
    concurrentSw.Stop();
    Trace.WriteLine(string.Format("Concurrent Dictionary lock test: {0} milliseconds", concurrentSw.ElapsedMilliseconds));
}

I'll take the simpler code and the performance gain. Great job MS on this new .NET class!

In an upcoming post I'll look into the use of the new BlockingCollection class in the Concurrent namespace. The class implements a new Producer-Consumer interface which is a pattern we reply upon in a few scenarios and where we have implemented the best practice ourselves. I am hoping this new collection will again simplify our code while improving efficiency.

Links

Wednesday, November 10, 2010

Visual Studio 2010: ASP.NET ControlBuilder MakeGeneric Workaround

Working with enumerations is very code friendly and we use them often especially when we are referencing a lookup table.  The ORM that we use (LLBLGen Pro) released its latest version recently and they make it even easier for us to map an enumeration to an entity field.

Sometimes these lookup tables need to be presented to the end user for whatever reason - be it a drop down, radio button list or a check box list.  When working in VS 2008 I stumbled onto this blog that outlines a straight forward way to create a generic drop down that exposes the the selected value as the specified generic Enum. Using that approach, we centralized all of the enum parsing that goes on for the gets and sets when working with the selected items. We took that implementation and created a generic dropdown and checkbox list.  It worked great in our environment at the time which was Visual Studio 2008.

Here is the checkbox list control that we were using:
public class EnumCheckBoxList<T> : EnumCheckBoxList where T : struct
{
    public List<T> SelectedEnumValues
    {
        get
        {
            List<T> items = new List<T>(base.Items.Count);
            foreach (ListItem item in base.Items)
            {
                if (item.Selected)
                    items.Add((T)Enum.Parse(typeof(T), item.Value));
            }
            return items;
        }
        set
        {
            base.ClearSelection();

            foreach (T item in value)
                base.Items.FindByValue(Enum.GetName(typeof(T), item)).Selected = true;
        }
    }

    /// <summary>
    /// Only valid for use on bit flagged enumerations
    /// </summary>
    public T? SelectedFlaggedEnumValues
    {
        get
        {
            ulong values = 0;
            foreach (ListItem item in base.Items)
            {
                if (item.Selected)
                    values = values | Convert.ToUInt64((T)Enum.Parse(typeof(T), item.Value));
            }
            return values == 0 ? null : (T?)Enum.Parse(typeof(T), values.ToString());
        }
        set
        {
            if (value != null)
            {
                Type enumType = typeof(T);
                foreach (Enum e in Enum.GetValues(enumType))
                {
                    ulong singleEnumFromFlagResult = 0;
                    if ((singleEnumFromFlagResult = (Convert.ToUInt64(e) & Convert.ToUInt64(value.Value))) > 0)
                    {
                        Enum singleEnumFromFlag = (Enum)Enum.Parse(enumType, singleEnumFromFlagResult.ToString());
                        base.Items.FindByValue(Enum.GetName(enumType, singleEnumFromFlag)).Selected = true;
                    }
                }
            }
        }
    }
}

[ControlBuilder(typeof(EnumCheckBoxListControlBuilder))]
public partial class EnumCheckBoxList : CheckBoxList
{
    public string EnumTypeName { get; set; }
}

public class EnumCheckBoxListControlBuilder : ControlBuilder
{
    public override void Init(TemplateParser parser, ControlBuilder parentBuilder, Type type, string tagName, string id,
                              System.Collections.IDictionary attribs)
    {

        string enumTypeName = (string)attribs["EnumTypeName"];
        Type enumType = Type.GetType(enumTypeName);
        if (enumType == null)
            throw new Exception(string.Format("Type for enum {0} can not be created", enumTypeName));

        Type dropDownType = typeof(EnumCheckBoxList<>).MakeGenericType(enumType);
        base.Init(parser, parentBuilder, dropDownType, tagName, id, attribs);
    }
}

Today our new development is done primarily in Visual Studio 2010.  We needed the same functionality for the generic dropdown and checkbox list for our Nucleus project but upon bringing over our implementation we discovered that the ControlBuilder wasn't updating the .designer file - so our controls weren't being converted to their generic implementations. After a little Googling, I found a bug entered in Microsoft Connect that described the issue we are having with VS 2010 and it seems that there isn't any workaround currently available for our situation using the ControlBuilder implementation.

The generic control was nice because programmers wouldn't have to go back and forth suppling plumbing to parse string values into enumerations and vise versa. We took the same idea and instead created a few extension methods.  The extension methods shown below have a get and set defined for a list of enumeration values and also a get and set for enumerations that are decorated with the FlagsAttribute.

/// <summary>
/// Get the selected enumeration values in the listbox
/// </summary>
/// <typeparam name="T">The type of enumeration represented in the listbox</typeparam>
/// <param name="list">The listbox</param>
/// <returns>Returns the list of selected enumeration values in the listbox</returns>
public static List<T> GetSelectedEnumValues<T>(this ListControl list) where T : struct
{
    List<T> items = new List<T>(list.Items.Count);
    foreach (ListItem item in list.Items)
    {
        if (item.Selected)
            items.Add((T)Enum.Parse(typeof(T), item.Value));
    }
    return items;
}

/// <summary>
/// Select the values in the given list of enums in the listbox.
/// </summary>
/// <typeparam name="T">The type of enumeration represented in the listbox</typeparam>
/// <param name="list">The listbox</param>
/// <param name="valuesToSelect">The enum values to select in the listbox</param>
public static void SetSelectedEnumValues<T>(this ListControl list, List<T> valuesToSelect) where T : struct
{
    list.ClearSelection();

    foreach (T item in valuesToSelect)
        list.Items.FindByValue(Enum.GetName(typeof(T), item)).Selected = true;
}

/// <summary>
/// Get the selected enumeration values in the listbox. This method should be called if the enumeration is decorated with the Flags attribute.
/// </summary>
/// <typeparam name="T">The type of enumeration represented in the listbox. This enumeration must be decorated with the Flags attribute</typeparam>
/// <param name="list">The listbox</param>
/// <returns>Returns the selected enumeration values in the listbox as a flagged enumeration. If no values are selected, null is returned</returns>
public static T? GetSelectedFlaggedEnumValues<T>(this ListControl list) where T : struct
{
    ulong values = 0;
    foreach (ListItem item in list.Items)
    {
        if (item.Selected)
            values = values | Convert.ToUInt64((T)Enum.Parse(typeof(T), item.Value));
    }
    return values == 0 ? null : (T?)Enum.Parse(typeof(T), values.ToString());
}

/// <summary>
/// Select the values in the given flag enumeration in the listbox.
/// </summary>
/// <typeparam name="T">The type of enumeration represented in the listbox. This enumeration must be decorated with the Flags attribute</typeparam>
/// <param name="list">The listbox</param>
/// <param name="valuesToSelect">The enum values (represented as Flags) to select in the listbox</param>
public static void SetSelectedFlaggedEnumValues<T>(this ListControl list, T valuesToSelect) where T : struct
{
    list.ClearSelection();

    Type enumType = typeof(T);
    foreach (Enum e in Enum.GetValues(enumType))
    {
        ulong singleEnumFromFlagResult = 0;
        if ((singleEnumFromFlagResult = (Convert.ToUInt64(e) & Convert.ToUInt64(valuesToSelect))) > 0)
        {
            Enum singleEnumFromFlag = (Enum)Enum.Parse(enumType, singleEnumFromFlagResult.ToString());
            list.Items.FindByValue(Enum.GetName(enumType, singleEnumFromFlag)).Selected = true;
        }
    }
}

Hopefully Microsoft responds to the Connect bug with a fix in an upcoming service pack.  In the mean time these extension methods will do the trick.

Links