简体   繁体   中英

Thread synchronization in C#

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);
        }
    }
}
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.

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);
        }
    }
}

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM