.NET Framework 4.6 and 4.5 – walk-through – (Part 2)

For full reference please visit: https://msdn.microsoft.com

Using generic collections is generally recommended, because you gain the immediate benefit of type safety without having to derive from a base collection type and implement type-specific members.

Generic collection types also generally perform better than the corresponding nongeneric collection types (and better than types that are derived from nongeneric base collection types) when the collection elements are value types, because with generics there is no need to box the elements.

Use the generic collection classes in the System.Collections.Concurrent namespace when multiple threads might be adding or removing items from the collection concurrently.

It is a good practice to implement IComparable on all classes are used as values in a list collection or as keys in a dictionary collection.

A Hashtable object consists of buckets that contain the elements of the collection. A bucket is a virtual subgroup of elements within the Hashtable, which makes searching and retrieving easier and faster than in most collections. Each bucket is associated with a hash code, which is generated using a hash function and is based on the key of the element.

A hash function is an algorithm that returns a numeric hash code based on a key. The key is the value of some property of the object being stored. A hash function must always return the same hash code for the same key. It is possible for a hash function to generate the same hash code for two different keys, but a hash function that generates a unique hash code for each unique key results in better performance when retrieving elements from the hash table.

For example, a hash function for a string might take the ASCII codes of each character in the string and add them together to generate a hash code.
The string “picnic” would have a hash code that is different from the hash code for the string “basket“; therefore, the strings “picnic” and “basket” would be in different buckets.
In contrast, “stressed” and “desserts” would have the same hash code and would be in the same bucket.

The ConcurrentDictionary<TKey, TValue> class should be used when multiple threads might be accessing the collection simultaneously.

Thread-Safe Collections

Multiple threads can safely and efficiently add or remove items from these collections, without requiring additional synchronization in user code. When you write new code, use the concurrent collection classes whenever the collection will be writing to multiple threads concurrently.

The .NET Framework 2.0 collection classes do not provide any thread synchronization; user code must provide all synchronization when items are added or removed on multiple threads concurrently.

We recommend the concurrent collections classes in the .NET Framework 4 because they provide not only the type safety of the .NET Framework 2.0 collection classes, but also more efficient and more complete thread safety than the .NET Framework 1.0 collections provide.

BlockingCollection<T> is a thread-safe collection class that provides the Producer-Consumer pattern.

Multiple threads or tasks can add items to the collection concurrently, and if the collection reaches its specified maximum capacity, the producing threads will block until an item is removed.

Here is example usage of BlockingCollection.

You can run at: https://dotnetfiddle.net/bn3PBZ

using System;
using System.Data;
using System.Collections.Concurrent;
using System.Threading.Tasks;
public class Data {

}

public class Program
{
public static void Process(Data data){
Console.WriteLine("Processing Data");
}
public static Data GetData(){
Data data = new Data();
return data;
}
public static void Main()
{
// A bounded collection. It can hold no more
// than 100 items at once.
BlockingCollection&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;Data&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; dataItems = new BlockingCollection&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;Data&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;(100);
// A simple blocking consumer with no cancellation.
Task.Run(() =&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;
{
while (!dataItems.IsCompleted)
{

Data data = null;
// Blocks if number.Count == 0
// IOE means that Take() was called on a completed collection.
// Some other thread can call CompleteAdding after we pass the
// IsCompleted check but before we call Take.
// In this example, we can simply catch the exception since the
// loop will break on the next iteration.
try
{
data = dataItems.Take();
}
catch (InvalidOperationException) { }

if (data != null)
{
Process(data);
}
}
Console.WriteLine("\r\nNo more items to take.");
});

// A simple blocking producer with no cancellation.
Task.Run(() =&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;
{
bool moreItemsToAdd = true;
int counter = 0;
while (moreItemsToAdd)
{
counter++;
Data data = GetData();
// Blocks if numbers.Count == dataItems.BoundedCapacity
dataItems.Add(data);
if(counter == 10){
moreItemsToAdd = false;
}
}
// Let consumer know we are done.
dataItems.CompleteAdding();
});
}
}

 You can cancel a loop by passing in a CancellationToken to the TryAdd or TryTake method, and then checking the value of the token’s IsCancellationRequested property on each iteration.

When to Use a Thread-Safe Collection

If performance is very important, then the best way to determine which collection type to use is to measure performance based on representative computer configurations and loads.

Pure producer-consumer scenario
Any given thread is either adding or removing elements, but not both.

Mixed producer-consumer scenario
Any given thread is both adding and removing elements.

Speedup
Faster algorithmic performance relative to another type in the same scenario.

Scalability
The increase in performance that is proportional to the number of cores on the computer. An algorithm that scales performs faster on eight cores than it does on two cores.

ConcurrentDictionary<TKey, TValue> is designed for multithreaded scenarios. You do not have to use locks in your code to add or remove items from the collection. However, it is always possible for one thread to retrieve a value, and another thread to immediately update the collection by giving the same key a new value.

Also, although all methods of ConcurrentDictionary<TKey, TValue> are thread-safe, not all methods are atomic, specifically GetOrAdd and AddOrUpdate. The user delegate that is passed to these methods is invoked outside of the dictionary’s internal lock. (This is done to prevent unknown code from blocking all threads.) Therefore it is possible for this sequence of events to occur:

1) threadA calls GetOrAdd, finds no item and creates a new item to Add by invoking the valueFactory delegate.

2) threadB calls GetOrAdd concurrently, its valueFactory delegate is invoked and it arrives at the internal lock before threadA, and so its new key-value pair is added to the dictionary.

3) threadA’s user delegate completes, and the thread arrives at the lock, but now sees that the item exists already

4) threadA performs a “Get”, and returns the data that was previously added by threadB.

Therefore, it is not guaranteed that the data that is returned by GetOrAdd is the same data that was created by the thread’s valueFactory. A similar sequence of events can occur when AddOrUpdate is called.

This example shows how to use a concurrent bag to implement an object pool. Object pools can improve application performance in situations where you require multiple instances of a class and the class is expensive to create or destroy. When a client program requests a new object, the object pool first attempts to provide one that has already been created and returned to the pool. If none is available, only then is a new object created.

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;


namespace ObjectPoolExample
{
    public class ObjectPool&amp;amp;amp;amp;amp;amp;lt;T&amp;amp;amp;amp;amp;amp;gt;
    {
        private ConcurrentBag&amp;amp;amp;amp;amp;amp;lt;T&amp;amp;amp;amp;amp;amp;gt; _objects;
        private Func&amp;amp;amp;amp;amp;amp;lt;T&amp;amp;amp;amp;amp;amp;gt; _objectGenerator;

        public ObjectPool(Func&amp;amp;amp;amp;amp;amp;lt;T&amp;amp;amp;amp;amp;amp;gt; objectGenerator)
        {
            if (objectGenerator == null) throw new ArgumentNullException("objectGenerator");
            _objects = new ConcurrentBag&amp;amp;amp;amp;amp;amp;lt;T&amp;amp;amp;amp;amp;amp;gt;();
            _objectGenerator = objectGenerator;
        }

        public T GetObject()
        {
            T item;
            if (_objects.TryTake(out item)) return item;
            return _objectGenerator();
        }

        public void PutObject(T item)
        {
            _objects.Add(item);
        }
    }

    class Program
    {
       static void Main(string[] args)
        {
            CancellationTokenSource cts = new CancellationTokenSource();

            // Create an opportunity for the user to cancel.
            Task.Run(() =&amp;amp;amp;amp;amp;amp;gt;
                {
                    if (Console.ReadKey().KeyChar == 'c' || Console.ReadKey().KeyChar == 'C')
                        cts.Cancel();
                });

            ObjectPool&amp;amp;amp;amp;amp;amp;lt;MyClass&amp;amp;amp;amp;amp;amp;gt; pool = new ObjectPool&amp;amp;amp;amp;amp;amp;lt;MyClass&amp;amp;amp;amp;amp;amp;gt; (() =&amp;amp;amp;amp;amp;amp;gt; new MyClass());            

            // Create a high demand for MyClass objects.
            Parallel.For(0, 1000000, (i, loopState) =&amp;amp;amp;amp;amp;amp;gt;
                {
                    MyClass mc = pool.GetObject();
                    Console.CursorLeft = 0;
                    // This is the bottleneck in our application. All threads in this loop
                    // must serialize their access to the static Console class.
                    Console.WriteLine("{0:####.####}", mc.GetValue(i));                 

                    pool.PutObject(mc);
                    if (cts.Token.IsCancellationRequested)
                        loopState.Stop();                 

                });
            Console.WriteLine("Press the Enter key to exit.");
            Console.ReadLine();
            cts.Dispose();
        }

    }

    // A toy class that requires some resources to create.
    // You can experiment here to measure the performance of the
    // object pool vs. ordinary instantiation.
    class MyClass
    {
        public int[] Nums {get; set;}
        public double GetValue(long i)
        {
            return Math.Sqrt(Nums[i]);
        }
        public MyClass()
        {
            Nums = new int[1000000];
            Random rand = new Random();
            for (int i = 0; i &amp;amp;amp;amp;amp;amp;lt; Nums.Length; i++)
                Nums[i] = rand.Next();
        }
    }   
}&amp;amp;amp;amp;amp;lt;/pre&amp;amp;amp;amp;amp;gt;

Let’s examine this example a little bit further;

When you a little think about this example you may arise a question:

What is the difference between pooling and singleton pattern?

In the following post https://community.oracle.com/thread/1659922?start=0&tstart=0 there is a good discussion around it. Here is some highlights:

When using a singleton, you have only one instance, and thus only one “utility class state”. This means that there is only one method or thread that should operate on that instance, unless it is designed to be multi-threaded and properly synchronized.

For example, a utility class that only contains methods, and no data is preserved in the object between calls. This is a candidate for a Singleton utility class.

This is a great example:

Now take an image cache class, that has the loadImage, getImage, and processImage methods. The design is such that a thread or method must assume that the image loaded by loadImage is the same that is used by the two other methods. In this case every method and thread that wants to use the class must acquire a separate instance. Object pooling may be an option.

statelessdoes not mean the same thing as immutable. If you need to maintain internal state you might consider using a pooling mechanism.

If you NEED to maintain a pool of reusable objects that have state and so are only SERIALLY reusable, then use a pool. Otherwise a singleton will do. Pool maintenance and check-out and return mechanisms cost a little bit.

Object pooling should only be used when there is a significant cost in construction of an object such as opening a socket, creating some complex structure, initializing a parser, etc. Otherwise just create separate instances when needed and toss references to them when done..

If there is no concurrency issue, then use a singleton. Might as well save the memory.

Well, we are right now quite sure what is going on between singleton and object pooling. So, another question arises:

Difference between Singleton Pattern vs Static Class

For example in Java world java.lang.Math is a static class.
java.lang.Runtime is a Singleton class. Even these names give a good idea of implementations points of those two structures. But let’s examine more;

Here is a good article about this http://javarevisited.blogspot.com/2013/03/difference-between-singleton-pattern-vs-static-class-java.html

If your Singleton is not maintaining any state, and just providing global access to methods, than consider using static class, as static methods are much faster than Singleton, because of static binding during compile time. But remember its not advised to maintain state inside static class, especially in concurrent environment, where it could lead subtle race conditions when modified parallel by multiple threads without adequate synchronization.

You can also choose to use static method, if you need to combine bunch of utility method together. Anything else, which requires singles access to some resource, should use Singleton design pattern.

 

Singleton represents object while static represent a method.

 

One more difference between Singleton and static is, ability to override. Since static methods cannot be overridden, they leads to inflexibility. On the other hand, you can override methods defined in Singleton class by extending it.

 

Static classes are hard to mock and consequently hard to test than Singletons, which are pretty easy to mock and thus easy to test. Because you can pass mock object whenever Singleton is expected, e.g. into constructor or as method arguments.

A Singleton class can implement an interface, which can come in handy when you want to separate implementation from API.

Another advantage of a singleton is that it can easily be serialized, which may be necessary if you need to save its state to disc, or send it somewhere remotely.

 

Singleton classes can be lazy loaded if its an heavy object, but static class doesn’t have such advantages and always eagerly loaded.

Leave a Reply

Your email address will not be published. Required fields are marked *