0

Here is the question:

Say there are 3 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.

What I tried:

 private static readonly Object obj = new Object();
    static List<string> list1 = new List<string> { "1", "2", "3", "4" };
    static List<string> list2 = new List<string> { "a", "b", "c", "d" };
    static List<string> list3 = new List<string> { "*", "+", "-", "?" };
    static int i = 0;
    static void Main(string[] args)
    {
        Thread t1 = new Thread(() => PrintItem());
        t1.Name = "Print1";
        Thread t2 = new Thread(() => PrintItem());
        t2.Name = "Print2";
        Thread t3 = new Thread(() => PrintItem());
        t3.Name = "Print3";
        t1.Start();
        t2.Start();
        t3.Start();
        t1.Join();
        t2.Join();
        t3.Join();
        Console.Read();
    }

    private static void PrintItem()
    {
        while (true)
        {
            lock (obj)
            {
                if (i >= list1.Count)
                    break;
                Console.WriteLine(Thread.CurrentThread.Name + " " + list1[i]);
                Console.WriteLine(Thread.CurrentThread.Name + " " + list2[i]);
                Console.WriteLine(Thread.CurrentThread.Name + " " + list3[i]);
                i++;
            }
        }
    }

The output is right but it doesn't use three threads. Correct the code please.

Hui Zhao
  • 655
  • 1
  • 10
  • 20
  • 1
    Your requirements prohibit any actions from ever being taken in parallel between your threads. There is only ever one thread at a time that can be doing anything. Given that, you're *far* better off just not having multiple threads, and using only *one* thread. That is, unless you can relax your requirements to actually let them work in parallel. – Servy Dec 11 '14 at 18:57
  • @Servy, I got this question from online. It is from [Novell Interview Question Tech Leads](http://www.careercup.com/question?id=16446668) – Hui Zhao Dec 11 '14 at 19:01
  • Okay. That doesn't make it an entirely insensible set of requirements to have. – Servy Dec 11 '14 at 19:06

3 Answers3

2

Although this is a very strange requirement, a nice scalable way, might be with Monitor.Pulse and Monitor.Wait

    private static readonly Object obj = new Object();
    static List<string> list1 = new List<string> { "1", "2", "3", "4" };
    static List<string> list2 = new List<string> { "a", "b", "c", "d", "e" };
    static List<string> list3 = new List<string> { "*", "+", "-", "?" };
    static int i = 0;

    //thread synchronization data
    const int numThreads = 3;
    static int workingCount = 0;
    static int lastItem = 0;
    static object locker = new object();

    static void Main(string[] args)
    {
        Thread t1 = new Thread(PrintItem);
        t1.Name = "Print1";
        Thread t2 = new Thread(PrintItem);
        t2.Name = "Print2";
        Thread t3 = new Thread(PrintItem);
        t3.Name = "Print3";
        t1.Start(0);
        t2.Start(1);
        t3.Start(2);
        t1.Join();
        t2.Join();
        t3.Join();
        Console.ReadLine();
    }

    private static void PrintItem(object state)
    {
        Interlocked.Increment(ref workingCount);
        int workingList = (int)state;
        int idx = 0;
        List<string> list = null;
        switch (workingList)
        {
            case 0:
                list = list1;
                break;
            case 1:
                list = list2;
                break;
            case 2:
                list = list3;
                break;
        }


        lock (locker)
            do
            {
                while ((lastItem % numThreads) != workingList)
                {
                    Monitor.Wait(locker);
                }

                Console.WriteLine("Thread: {0}\tValue: {1}", Thread.CurrentThread.Name, list[idx]);
                lastItem++;
                Monitor.PulseAll(locker);

            } while (++idx < list.Count);

        //Handle continuing to pulse until all lists are done.
        Interlocked.Decrement(ref workingCount);

        lock (locker)
            while (workingCount != 0)
            {
                while ((lastItem % numThreads) != workingList)
                    Monitor.Wait(locker);
                lastItem++;
                Monitor.PulseAll(locker);
            }

    }
}

enter image description here

bigtlb
  • 1,512
  • 10
  • 16
  • This assumes that all lists are the same size. Otherwise we would need to add a little logic to keep the finished threads continuing the plus, until all lists were done. Probably with an InterlockedIncrement countdown. – bigtlb Dec 11 '14 at 19:57
  • The thread name was not printed. Ex: `Print1`. But I just up voted you because concise code. – Hui Zhao Dec 11 '14 at 20:02
  • @HuiZhao `Thread.CurrentThread.Name` does print the current thread name. I will include a screenshot above. – bigtlb Dec 11 '14 at 20:05
  • Oops, crap. Sorry about it. I forgot to set as the project as the startup one. It run the old one. – Hui Zhao Dec 11 '14 at 20:11
  • No worries. If your lists might be of different lengths, I have modified the code to gracefully spin down. So if List1 runs out of items, then List 2 and 3 will continue... – bigtlb Dec 11 '14 at 20:16
1

If it was me I would use EventWaitHandles to signal and synchronize your threads. In my code the Threads wait for a Signal and they then print out their current string and then wait to be signaled again:

    static void Main(string[] args)
    {
        List<string> list1 = new List<string> { "1", "2", "3", "4" };
        List<string> list2 = new List<string> { "a", "b", "c", "d" };
        List<string> list3 = new List<string> { "*", "+", "-", "?" };


        using (EventWaitHandle waitHandle1 = new AutoResetEvent(false))
        using (EventWaitHandle waitHandle2 = new AutoResetEvent(false))
        using (EventWaitHandle waitHandle3 = new AutoResetEvent(false))
        using (EventWaitHandle waitHandle4 = new AutoResetEvent(false))
        {
            Thread t1 = new Thread(() => 
            {
                ThreadData state = new ThreadData() 
                { 
                    Name = "Thread1", 
                    Strings = list1, 
                    WaitHandle = waitHandle1, 
                    SignalHandle = waitHandle2 
                };

                PrintItemWhenSignaled(state);
            });

            Thread t2 = new Thread(() => 
            {
                ThreadData state = new ThreadData()
                {
                    Name = "Thread2",
                    Strings = list2,
                    WaitHandle = waitHandle2,
                    SignalHandle = waitHandle3
                };

                PrintItemWhenSignaled(state);
            });

            Thread t3 = new Thread(() =>
            {
                ThreadData state = new ThreadData()
                {
                    Name = "Thread3",
                    Strings = list3,
                    WaitHandle = waitHandle3,
                    SignalHandle = waitHandle4
                };

                PrintItemWhenSignaled(state);
            });


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

            for (int index = 0; index < list1.Count; index++)
            {
                waitHandle1.Set();
                waitHandle4.WaitOne(100);
            }
        }

        Console.WriteLine("Press any key...");
        Console.ReadKey();
    }

    private static void PrintItemWhenSignaled(ThreadData threadState)
    {
        foreach (string value in threadState.Strings)
        {
            threadState.WaitHandle.WaitOne(100);
            Console.WriteLine("{0}:{1}", threadState.Name, value);
            threadState.SignalHandle.Set();
        }
    }

    public class ThreadData
    {
        public string Name { get; set; }
        public EventWaitHandle WaitHandle { get; set; }
        public EventWaitHandle SignalHandle { get; set; }
        public List<string> Strings { get; set; }
    }
}
Mike Burdick
  • 838
  • 6
  • 5
  • If we have 10000 items in the list, do we need to define 10000 `EventWaitHandle`? – Hui Zhao Dec 11 '14 at 19:16
  • No, only each thread you create requires a EventWaitHandle and the calling Thread needs a EventWaitHandle so in your example you need 4. – Mike Burdick Dec 11 '14 at 19:28
  • Can you update the code? I want to print the thread name or Id to verify it is using three threads. The current code only print the values. Also I would like use lambada expression. – Hui Zhao Dec 11 '14 at 19:31
  • You could do this with just three wait handles if you make thread 3 notify `WaitHandle1`. That way the main thread doesn't have to get involved. If the main thread needs to know when all threads have finished, all it has to do is `Join` the last thread. – Jim Mischel Dec 11 '14 at 19:44
-1

because of the Lock only one thread ... the fastes one is the first one ... can access your list.

remove the lock statement or remove the:

        if (i >= list1.Count)
                break;

condition, or make i ThreadStatic with the Attribute ThreadStatic

if this your expected output? enter image description here

Venson
  • 1,772
  • 17
  • 37
  • It doesn't work, remove `lock` will cause exception. I tested it. – Hui Zhao Dec 11 '14 at 18:58
  • What kind of exeption? try making the variable ThreadStatic so every thread will have an uniq variable of its own. this code will run only once through the list and when the next thread trys to execute the code the condition will cause an break. – Venson Dec 11 '14 at 19:00
  • I updated the post to show you the usage of this Attribute and the Output i got – Venson Dec 11 '14 at 19:04
  • The code is not safe, and does not (reliably) produce the required output without any synchronization. – Servy Dec 11 '14 at 19:07
  • Please explain more detailed i do not understand ... when i read the description this is what i would expect. Try creating an ProcessDiagram – Venson Dec 11 '14 at 19:07