Thursday, March 29, 2007

Thread Safety In .NET Collections

Most .NET collections are not thread-safe. Take generic list as an example, the MSDN description is as following:

Thread Safety

Public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.

A List<T> can support multiple readers concurrently, as long as the collection is not modified. Enumerating through a collection is intrinsically not a thread-safe procedure. In the rare case where an enumeration contends with one or more write accesses, the only way to ensure thread safety is to lock the collection during the entire enumeration. To allow the collection to be accessed by multiple threads for reading and writing, you must implement your own synchronization.

So we need to implement read/write locks in multi-threaded environment when the list objects are modified. That's a lot of extra work and is prone to deadlock in a complicate system.

Is there any easy way on this? Can we build a new collection and switch the list reference to the new constructed collection? Following code demos such approach:
using System;
using System.Collections.Generic;
using System.Threading;

class Test
{
public class Person
{
public string Name;
public int Age;
public Person(string name, int age)
{
Name = name;
Age = age;
}
}

private static List<Person> _personList = new List<Person>();
static void Main()
{
// Build initial list
for (int i = 0; i < 10; i++)
{
_personList.Add(new Person("Name" + i.ToString(), i));
}

new Thread(new ThreadStart(LoopthroughList)).Start();

//Following thread will throw InvalidOperationException:
//Exception: Collection was modified; enumeration operation may not execute.
//new Thread(new ThreadStart(UpdateListUnsafely)).Start();

// Following thread won't throw exception
new Thread(new ThreadStart(UpdateListSafely)).Start();

Console.Read();
}

static void LoopthroughList()
{
Console.WriteLine("First loop");
foreach (Person person in _personList)
{
Console.WriteLine("ListHashCode:{0} ListCount:{1} Name:{2} Age:{3}",
_personList.GetHashCode(), _personList.Count, person.Name, person.Age);
Thread.Sleep(100);
}
Console.WriteLine("Second loop");
foreach (Person person in _personList)
{
Console.WriteLine("ListHashCode:{0} ListCount:{1} Name:{2} Age:{3}",
_personList.GetHashCode(), _personList.Count, person.Name, person.Age);
}
}

static void UpdateListUnsafely()
{
Thread.Sleep(500);
_personList.RemoveAt(_personList.Count - 1);
_personList.Add(new Person("A New Person", 20));
}

static void UpdateListSafely()
{
Thread.Sleep(500);
List<Person> newList = new List<Person>(_personList);
newList.RemoveAt(newList.Count - 1);
newList.RemoveAt(newList.Count - 1);
newList[newList.Count - 1].Name = "Modified Name";
newList.Add(new Person("A New Person", 20));
_personList = newList;
Console.WriteLine("List updated! ListCount:{0}", _personList.Count);
}
}
Result:
The result looks promising. We don't see any error when switching the reference (theoretically we should lock this operation). Is such implementation really thread-safe now? Maybe yes maybe no. The problem is that I couldn't find an official answer from Microsoft.

Thursday, March 15, 2007

ASP.NET Find Control Recursively

Sometimes we need to get a control instance inside a page. Looking up each control recursively is the easiest way:
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
LoginInfoUserControl loginControl = FindControl<LoginInfoUserControl>(this.Page, "ucLogin");
}

public static T FindControl<T>(Control root, string controlID) where T : class
{
if (root is T && root.ID == controlID)
{
return root as T;
}
foreach (Control control in root.Controls)
{
T foundControl = FindControl<T>(control, controlID);
if (foundControl != null)
{
return foundControl;
}
}
return null;
}
}

Wednesday, March 07, 2007

HttpWebRequest With Cookie Context

I was doing some load test on an ASP.NET page. The page uses Cache to store some profile data, and the Cache is associated with the client Cookie value. The load test tools I have don't have such dynamic cookie assignment functionality. So I created a simple load test tool for this. The key point is to assign the CookieContainer property of the HttpWebRequest object:
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;

namespace HttpRequestTest
{
class Program
{
static void Main(string[] args)
{
string url = "http://localhost/MyDashBoard.aspx";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET";
CookieContainer cookieContainer = new CookieContainer();
Cookie cookie = new Cookie("cookieKey", "cookieValue");
cookie.Path= "/";
cookie.Domain = "localhost";
cookieContainer.Add(cookie);
request.CookieContainer = cookieContainer;

HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream responseData = response.GetResponseStream();
StreamReader reader = new StreamReader(responseData);
string responseFromServer = reader.ReadToEnd();

Console.Read();

}
}
}