1

I have 2 thread (in printer and in counter class).the counter class updates the property in storage and printer prints it.Now i want to print the updated values by counter only once. so how do i stop the execution of my printer thread after printing the last updated number. It prints the last number sometimes once or sometimes more than once. Basically what I need is to update a property and every time that property is updated I need to print the updated value on the console and the printer thread doesn't know the no of updates that are going to take place. So it should stop as and when the updating thread stops updating.

The code is like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace Threads
{
    class Storage
    {
        static int _number;
        public readonly static object LockNumber = new object();
        public static int Number
        {
            get
            {
                lock(LockNumber)
                {
                    Monitor.Pulse(LockNumber);
                    return _number;
                }
            }
            set
            {
                lock(LockNumber)
                {
                    _number = value;
                    Monitor.Pulse(LockNumber);
                    Monitor.Wait(LockNumber);
                }
            }
        }
    }

    class Counter
    {

        public Thread t = new Thread(new ThreadStart(CounterFunction));

        public Counter()
        {
            t.Start();
        }
        public static void CounterFunction()
        {
            for (int i = 0; i < 25; i++)
            {
                Storage.Number = i;
            }
        }
    }

    class Printer
    {
        public Thread t1 = new Thread(new ThreadStart(Print));

        public Printer()
        {
            t1.Start();
        }
        public static void Print()
        {
            while (true)
            {
                Console.WriteLine("Number is " + Storage.Number);
            }
        }
    }

    class Check
    {
        static void Main()
        {
            Storage s1 = new Storage();
            Counter c = new Counter();
            Printer p = new Printer();

            c.t.Join();
            if (!c.t.IsAlive)
            {
                p.t1.Abort();
            }
            Thread.Sleep(10000);
        }
    }
}
Siddhant
  • 571
  • 1
  • 8
  • 32

3 Answers3

1
public static void Print()
{
    int prevNumber = Storage.Number;
    while (true)
    {
        int number = Storage.Number;
        if (number !=prevNumber) {   
            Console.WriteLine("Number is " + number);
            prevNumber = number;
        }
    }
}

This should help. Though this is busy waiting and will consume 100% of your processor. No real application should do it like this.

Dariusz
  • 21,561
  • 9
  • 74
  • 114
  • but my printer thread wont terminate even after the counter thread is done updating.. even in this case – Siddhant Nov 26 '13 at 12:56
  • basically i want my printer thread to stop executing after printing the last updated number..my program is working fine for printing the updated numbers.The problem is that the last number is printed unknown number of times depending upon the time it takes to abort the printer thread – Siddhant Nov 26 '13 at 12:58
  • @user2991609 How does the printer know the updating has finished? It does not, as of right now. You have to communicate it somehow. Think on it yourself. – Dariusz Nov 26 '13 at 13:26
0

In the Printer class, add an AutoResetEvent member. On the printer thread, WaitOne on it. This will block without busy wait. When the property you are watching is updated, raise an event handled by the Printer class. In the handler, Set the AutoResetEvent. This will unblock the printer thread.

Something like this:

class Storage
{
    internal event Action<bool> NumberUpdated;
    {
        set
        {
            lock(LockNumber)
            {
                _number = value;
                if( NumberUpdated != null )
                   NumberUpdated( isLastUpdate ); //TODO: Add logic to compute it
                Monitor.Pulse(LockNumber);
                Monitor.Wait(LockNumber);
            }
        }
    }
}

class Printer
{
    private AutoResetEvent propertyUpdated;
    private bool keepPrinting;

    //Other code omitted for brevity's sake
    public Printer( Storage storage )
    {
        propertyUpdated = new AutoResetEvent();
        storage.NumberUpdated += OnStorageNumberUpdated;
        keepPrinting = true;
        t1.Start();
    }

    private void OnStorageNumberUpdated( bool isLastUpdate ){
       keepPrinting = !isLastUpdate;
       propertyUpdated.Set();
    }

    public static void Print()
    {
        while (keepPrinting)
        {
            propertyUpdated.WaitOne();
            Console.WriteLine("Number is " + Storage.Number);
        }
    }
}
dario_ramos
  • 7,118
  • 9
  • 61
  • 108
  • how do i determine when to stop executing printer thread even if i use AutoResetEvent..AutoResetEvent will only help me notify my printer thread to print the value..but it wont stop it when the last number is printed..it will still be in the waiting queue. – Siddhant Nov 26 '13 at 13:01
  • block here means? suspended? – Siddhant Nov 26 '13 at 13:02
  • If you want to do that, you can add a boolean parameter to the `NumberUpdated` event (make it an `Action`) which tells whether the update is the last one. Sorry, I missed that part. And yes, blocking here means suspending execution until the `AutoResetEvent` is set from another thread. Said blocking will not waste cpu cycles. – dario_ramos Nov 26 '13 at 13:09
  • I updated the code with my last proposal. It's missing a way to determine from the Storage class whether the notified update is the last one or not. You might know better about that. – dario_ramos Nov 26 '13 at 13:14
0

It can easily be handled by giving Storage.Number the number -1 when there is no more updates and adding an if statement at printer:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace Threads
{
    class Storage
    {
        static int _number;
        public readonly static object LockNumber = new object();
        public static int Number
        {
            get
            {
                lock (LockNumber)
                {
                    Monitor.Pulse(LockNumber);
                    return _number;
                }
            }
            set
            {
                lock (LockNumber)
                {
                    _number = value;
                    Monitor.Pulse(LockNumber);
                    Monitor.Wait(LockNumber);
                }
            }
        }
    }

    class Counter
    {

        public Thread t = new Thread(new ThreadStart(CounterFunction));

        public Counter()
        {
            t.Start();
        }
        public static void CounterFunction()
        {
            for (int i = 0; i < 25; i++)
            {
                Storage.Number = i;
            }
            Storage.Number = -1;
        }
    }

    class Printer
    {
        public Thread t1 = new Thread(new ThreadStart(Print));

        public Printer()
        {
            t1.Start();
        }
        public static void Print()
        {
            Boolean stop = false;
            while (!stop)
            {
                if (Storage.Number != -1)
                {
                    Console.WriteLine("Number is " + Storage.Number);
                }
                else
                {
                    stop = true;
                }
            }
        }
    }

    class Check
    {
        static void Main()
        {
            Storage s1 = new Storage();
            Counter c = new Counter();
            Printer p = new Printer();

            c.t.Join();
            if (!c.t.IsAlive)
            {
                p.t1.Abort();
            }
            Thread.Sleep(10000);
        }
    }
}

Though this will still result in some numbers being printed twice. So we're starting the threads from the main class (Check) and use an event handler to stop the threads. Also we'll save the last value in Printer so that none of the values is printed twice (printing is mostly a lot faster then incrementing).

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace Threads
{
    class Storage
    {
        static int _number;
        public readonly static object LockNumber = new object();
        public static int Number
        {
            get
            {
                lock (LockNumber)
                {
                    Monitor.Pulse(LockNumber);
                    return _number;
                }
            }
            set
            {
                lock (LockNumber)
                {
                    _number = value;
                    Monitor.Pulse(LockNumber);
                    Monitor.Wait(LockNumber);
                }
            }
        }
    }

    class Counter
    {
        public delegate void Done();
        public event Done OnDone;

        public Counter()
        {
            //t.Start();
        }
        public void CounterFunction()
        {
            for (int i = 0; i < 25; i++)
            {
                Storage.Number = i;
            }
            Storage.Number = -1;
            if (OnDone != null)
            {
                OnDone();
            }
        }
    }

    class Printer
    {
        public Printer()
        {
            //t1.Start();
        }
        public void Print()
        {
            Boolean stop = false;
            int prevNumber = -1;
            while (!stop)
            {
                if (Storage.Number != -1)
                {
                    if (Storage.Number != prevNumber)
                    {
                        prevNumber = Storage.Number;
                        Console.WriteLine("Number is " + Storage.Number);
                    }
                }
                else
                {
                    stop = true;
                }
            }
        }
    }

    public partial class Check : Form //Invoking is a System.Windows.Forms function
    {
        public Thread _cThread;
        public Thread _pThread;

        static void Main()
        {
            Check ch = new Check();
        }

        public Check()
        {
            Storage s1 = new Storage();
            Counter c = new Counter();
            c.OnDone += new Counter.Done(countDone);
            Printer p = new Printer();

            _cThread = new Thread(new ThreadStart(c.CounterFunction));
            _pThread = new Thread(new ThreadStart(p.Print));
            _cThread.Start();
            _pThread.Start();
            while (true) ; //This is only here so that you can see the results.
        }

        private void countDone()
        {
            if (_pThread.IsAlive)
            {
                _pThread.Abort();
            }
            //Close the threads nicely
            if (this.InvokeRequired)
            {
                this.Invoke(new MethodInvoker(this.countDone)); //This says: invoke and then call countDone.
            }
        }
    }
}

You will need to reference System.Windows.Forms

Steven H
  • 39
  • 10
  • can you tell me what is the purpose of InvokeRequired and MethodInvoker? I am confusing with this part – Siddhant Nov 27 '13 at 05:20