Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to access list elements in each thread sequentially

Recently I was asked this question in an phone interview

"Say there are 3 array lists l1, l2 & l3 of same length. Three threads accessing three lists. Say T1 -> l1, T2 ->l2 & T3 ->l3. It should print in the order say first element of 1st then first element of 2nd list and then first element of 3rd list. Then second element of 1st then second element of 2nd list and then second element of 3rd list."

I replied that using semaphore can solve this problem but when I tried myself with semaphore could not get the proper answer. What is wrong in my below code

namespace Semaphore_Example
{
    class Program
    {
        static List<int> list1 = new List<int>{ 1, 2, 3, 4, 5 };
        static List<int> list2 = new List<int> { 1, 2, 3, 4, 5 };
        static List<int> list3 = new List<int> { 1, 2, 3, 4, 5 };

        static Semaphore semaphore = new Semaphore(0,3);
        static SemaphoreSlim _sem = new SemaphoreSlim(3);    

        static void Main(string[] args)
        {

            Thread t1 = new Thread(show);
            Thread t2 = new Thread(show);
            Thread t3 = new Thread(show);

            t1.Start();
            t2.Start();
            t3.Start();

            Console.ReadLine();
        }

        static void show()
        {
            _sem.Wait();

            for (int i = 0; i < 5; i++)
            {
                Console.WriteLine(list1[i]);
                Console.WriteLine(list2[i]);
                Console.WriteLine(list3[i]);
            }

            _sem.Release();

        }
    }
}
like image 530
sia Avatar asked Feb 15 '15 09:02

sia


1 Answers

A semaphore by itself would not be suitable for your requirements. A semaphore can synchronize access to a given resource, but it does not maintain order among the threads that attempt to access it. Per MSDN:

There is no guaranteed order, such as FIFO or LIFO, in which blocked threads enter the semaphore.

Instead, I would suggest that you use a set of wait handles, one per thread, such that each thread waits on its own handle before printing each element, and signals the next thread's handle after doing so. The sample below is generalized to work with any number of lists (threads).

static List<string> list1 = new List<string> { "A1", "A2", "A3", "A4", "A5" };
static List<string> list2 = new List<string> { "B1", "B2", "B3", "B4", "B5" };
static List<string> list3 = new List<string> { "C1", "C2", "C3", "C4", "C5" };

// Add all lists to the array below.
static List<string>[] lists = new[] { list1, list2, list3 };
static AutoResetEvent[] waitHandles;

static void Main(string[] args)
{
    waitHandles = new AutoResetEvent[lists.Length];
    var threads = new Thread[lists.Length];

    for (int i = 0; i < lists.Length; i++)
    {
        // Initialize a wait handle and thread for each list.
        int threadIndex = i;
        waitHandles[i] = new AutoResetEvent(false);
        threads[i] = new Thread(new ThreadStart(() => show(threadIndex)));
        threads[i].Start();
    }

    // Signal first thread to start off process.
    waitHandles[0].Set();

    Console.ReadLine();
}

// Method run within each thread.
static void show(int threadIndex)
{
    // The index of the next thread to signal after printing each element.
    int nextThreadIndex = (threadIndex + 1) % lists.Length;

    // Iterate over all elements of current thread's list.
    foreach (string x in lists[threadIndex])
    {
        // Wait for turn of current thread.
        waitHandles[threadIndex].WaitOne();

        // Print one element.
        Console.Write(x + " ");

        // Signal next thread to proceed.
        waitHandles[nextThreadIndex].Set();
    }

    // Assume all lists are equal in length.
    // Otherwise, threads might need to keep signalling
    // even after printing all their elements.
}

// Output: A1 B1 C1 A2 B2 C2 A3 B3 C3 A4 B4 C4 A5 B5 C5
like image 131
Douglas Avatar answered Nov 15 '22 16:11

Douglas