Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Threadsafe collection without lock

I am preparing myself for an interview and I came across the followign question. I tried but I could not find anything which can create a class containing thread safe collection without "lock". If know any solution then please help.

Create a C# class derived from Object and implements the following methods:

  • AddString – This method should add a given string to an internal collection
  • ToString – Override this method and return a single, comma-delimited string containing all the strings in the internal collection

Requirements:

  • Must be thread-safe
  • Must support multiple concurrent readers
  • Must not use any pre-existing thread-safe collections
  • Bonus: don’t use any type of lock
like image 904
Mayur Avatar asked May 20 '12 17:05

Mayur


People also ask

Do you need to lock a ConcurrentQueue?

In general Queue you need a lock statement (which locks entire queue for a thread) and then you Enqueue/Dequeue. While for ConcurrentQueue you do not need to specifically lock as the to Enqueue/Dequeue would have the necessary item level locks. "Simple queue" is usually the problem.

How do I make a collection thread-safe?

A thread-safe variant of ArrayList in which all mutative operations (e.g., add, set, remove..) are implemented by creating a separate copy of an underlying array. It achieves thread safety by creating a separate copy of the List which is different than vector or other collections used to provide thread-safety.

What all collections are thread-safe?

The collection classes that are thread-safe in Java are Stack, Vector, Properties, Hashtable, etc.

Are collections thread-safe?

All collection classes (except Vector and Hashtable) in the java. util package are not thread-safe. The only two legacy collections are thread-safe: Vector and Hashtable.


2 Answers

Here’s a way of achieving lock-free modification of a collection by working on a local copy and then attempting to atomically swap it with the global collection whilst checking for races:

public class NonLockingCollection
{
    private List<string> collection;

    public NonLockingCollection()
    {
        // Initialize global collection through a volatile write.
        Interlocked.CompareExchange(ref collection, new List<string>(), null);
    }

    public void AddString(string s)
    {
        while (true)
        {
            // Volatile read of global collection.
            var original = Interlocked.CompareExchange(ref collection, null, null);

            // Add new string to a local copy.
            var copy = original.ToList();
            copy.Add(s);

            // Swap local copy with global collection,
            // unless outraced by another thread.
            var result = Interlocked.CompareExchange(ref collection, copy, original);
            if (result == original)
                break;
        }
    }

    public override string ToString()
    {
        // Volatile read of global collection.
        var original = Interlocked.CompareExchange(ref collection, null, null);

        // Since content of global collection will never be modified,
        // we may read it directly.
        return string.Join(",", original);
    }
}

Edit: Since using Interlocked.CompareExchange to implicitly perform volatile reads and writes has given rise to some confusion, I’m posting below the equivalent code with Thread.MemoryBarrier calls instead.

public class NonLockingCollection
{
    private List<string> collection;

    public NonLockingCollection()
    {
        // Initialize global collection through a volatile write.
        collection = new List<string>();
        Thread.MemoryBarrier();
    }

    public void AddString(string s)
    {
        while (true)
        {
            // Fresh volatile read of global collection.
            Thread.MemoryBarrier();
            var original = collection;
            Thread.MemoryBarrier();

            // Add new string to a local copy.
            var copy = original.ToList();
            copy.Add(s);

            // Swap local copy with global collection,
            // unless outraced by another thread.
            var result = Interlocked.CompareExchange(ref collection, copy, original);
            if (result == original)
                break;
        }
    }

    public override string ToString()
    {
        // Fresh volatile read of global collection.
        Thread.MemoryBarrier();
        var original = collection;
        Thread.MemoryBarrier();

        // Since content of global collection will never be modified,
        // we may read it directly.
        return string.Join(",", original);
    }
}
like image 164
Douglas Avatar answered Nov 15 '22 21:11

Douglas


Based on the question you should be able to add a concurrent collection inside your object that will handle the thread safety requirements for you. They did not specify what type of internal collection to use.

You should be able to implement one of the collections from the concurrentcollection namespace and achieve this.

http://msdn.microsoft.com/en-us/library/system.collections.concurrent.aspx

like image 21
tsells Avatar answered Nov 15 '22 22:11

tsells