Thursday, November 11, 2010

Thread Synchronization Techniques – MethodImplAttribute

  • This is one more thread synchronization technique where you decorate a method with a particular attribute. We use attribute MethodImplAttribute with option of MethodImplOptions.Synchronized to make the whole method thread safe.
  • MethodImplAttribute is in System.Runtime.CompilerServices namespace.
  • This attribute option ensures that the method can be executed by only one thread at a time.
  • Note that this class lies under CompilerServices namespace. So when you compile your app, compiler inserts lock statements around the body of the method to make the whole method thread safe. For the instance method it uses lock(this) and for the static methods it uses lock(typeof(classname)). Because of this, the use of this approach for thread synchronization is discouraged. Note that it is usually not a good practice to use lock on publicly visible objects/types. This is because somebody else might, in the future, use the same object/type to lock some other code segment, thus decreasing concurrency and increasing the chance of deadlock.

Following code snippet shows the use of MethodImplAttribute to synchronize two threads. We have a DataStore class which is accessed by two threads to store data. In the AddData method of DataStore, we simulate a delay in data processing by using Thread.Sleep. Here if we remove the MethodImplAttribute from the AddData method, you will randomly get System.ArgumentException. Following is the sample application:

using System;
using System.Threading;
using System.Reflection;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Collections.Generic;

namespace ConsoleApplication1
{
class DataStore
{
private Dictionary<string, string> dataCollection = new Dictionary<string, string>();
private const int StoreLimit = 100000;

[MethodImplAttribute(MethodImplOptions.Synchronized)]
public void AddData(string data)
{
if (!dataCollection.ContainsKey(data) && dataCollection.Count < StoreLimit)
{
Thread.Sleep(1000); // simulate some delay for data processing
dataCollection.Add(data, data);

}
}
}
class Program
{
static void Main(string[] args)
{
(new Thread(ReceiveDataFromServer1)).Start();
(new Thread(ReceiveDataFromServer2)).Start();
}

private static Random rand = new Random();
private static DataStore store = new DataStore();
private static void ReceiveDataFromServer1()
{
while (true)
{
string data = rand.Next(10, 20).ToString();
store.AddData(data);
Console.WriteLine("First thread data - " + data);
}
}

private static void ReceiveDataFromServer2()
{
while (true)
{
string data = rand.Next(10, 20).ToString();
store.AddData(data);
Console.WriteLine("Second thread data - " + data);
}
}
}
}

No comments: