3. Concurrency issues with threads

Keywords: PHP

1. Simulate the concurrency of multiple ATM machines accessing money simultaneously using multithreading

class Account

    {

        //holder

        private string _holderName;

        public string HolderName

        {

            get { return _holderName; }

            set { _holderName = value; }

        }

 

        //Password

        private string _password;

        public string Password

        {

            get { return _password; }

            set { _password = value; }

        }

 

        //Account balance

        private float _amount;

        public float Amount

        {

            get { return _amount; }

            set { _amount = value; }

        }

 

        /// <summary>

        /// Constructor

        /// </summary>

        /// <param name="holdderName"></param>

        /// <param name="password"></param>

        /// <param name="amount"></param>

        public Account(string holdderName, string password, float amount)

        {

            this._holderName = holdderName;

            this._password = password;

            this._amount = amount;

        }

 

        //deposit

        public void Deposit(float amount)

        {

            if (amount > 0)

            {

                this._amount = this._amount + amount;

            }

        }

 

        //Withdraw money

        public void Withdraw(float amount)

        {

            if (this._amount >= amount && amount > 0)

            {

                this._amount = this._amount - amount;

            }

        }

        //Get Balance

        public float CheckBalance()

        {

            return this._amount;

        }

}

 

class ATM

    {

        //Account number

        public Account account;

 

        //deposit

        public void Deposit(float amount)

        {

            account.Deposit(amount);

        }

 

        //Withdraw money

        public void WithDraw(float amount)

        {

            account.Withdraw(amount);

        }

 

        //Get Balance

        public float GetBalance()

        {

            return account.CheckBalance();

        }

    }

 

 

class Program

    {

 

        Account acc = new Account("Zhang Lan", "0000", 5000);

 

        static void Main(string[] args)

        {

            Thread[] threads = new Thread[10];

            Program p = new Program();

 

            for (int i = 0; i < 10; i++)

            {

                threads[i] = new Thread(new ThreadStart(p.Run));

                threads[i].Name = "thread" + (i + 1);

            }

 

            foreach (Thread t in threads)

            {

                t.Start();

            }

 

            Console.ReadKey();

        }

 

        public void Run()

        {

 

            ATM atm = new ATM();

            atm.account = acc;

 

 

            Console.WriteLine(Thread.CurrentThread.Name +

                 ": Query Current" + atm.GetBalance());

 

            Console.WriteLine(Thread.CurrentThread.Name +

                 ": Draw 2000");

            //Withdraw money

            atm.WithDraw(2000);

 

            Thread.Sleep(100);

 

            Console.WriteLine(Thread.CurrentThread.Name +

                 ": Query Current" + atm.GetBalance());

 

            Console.WriteLine(Thread.CurrentThread.Name +

                 ": Deposit 2000");

            //deposit

            atm.Deposit(2000);

            Console.WriteLine(Thread.CurrentThread.Name +

                 ": Query Current" + atm.GetBalance());

 

        }

    }

 

 

 

 

 

 

 

 

We can find that 10 people deposit 2000 and withdraw 2000 at the same time, and the final balance should be unchanged, but the results are confusing, indicating that multiple threads are alternating, and concurrency problems occur.

 

2. Solutions

(1) Use lock to solve concurrency problems

The lock keyword marks a statement block as a critical zone by acquiring a mutex on a given object, executing the statement, and releasing the lock.The form of this statement is as follows:

Object thisLock = new Object();

lock (thisLock)

{

// Critical code section.

}
The lock keyword ensures that when one thread is in a critical zone of code, another thread does not enter that critical zone.If another thread attempts to enter locked code, it will wait (that is, be blocked) until the object is released.

 

The Lock keyword means: code that allows you to define a thread synchronization, and its syntax is:

 

 

Put some code in the lock block and it's safe

 

The code block to add here is the operation code for withdrawal and deposit put in the lock block

 

Define an object of type object first

 

 

Next we put the code for the operation account in the lock block:

 

 class Program

    {

        public static readonly object _lockObject = new object();

 

        Account acc = new Account("Zhang Lan", "0000", 5000);

 

        static void Main(string[] args)

        {

            Thread[] threads = new Thread[10];

            Program p = new Program();

 

            for (int i = 0; i < 10; i++)

            {

                threads[i] = new Thread(new ThreadStart(p.Run));

                threads[i].Name = "thread" + (i + 1);

            }

 

            foreach (Thread t in threads)

            {

                t.Start();

            }

 

            Console.ReadKey();

        }

 

        public void Run()

        {

 

            ATM atm = new ATM();

            atm.account = acc;

 

            //Lock quota-related operation codes

            lock (_lockObject)

            {

 

                Console.WriteLine(Thread.CurrentThread.Name +

                     ": Query Current" + atm.GetBalance());

 

                Console.WriteLine(Thread.CurrentThread.Name +

                     ": Draw 2000");

                //Withdraw money

                atm.WithDraw(2000);

 

                Thread.Sleep(100);

 

                Console.WriteLine(Thread.CurrentThread.Name +

                     ": Query Current" + atm.GetBalance());

 

                Console.WriteLine(Thread.CurrentThread.Name +

                     ": Deposit 2000");

                //deposit

                atm.Deposit(2000);

                Console.WriteLine(Thread.CurrentThread.Name +

                     ": Query Current" + atm.GetBalance());

 

            }

        }

    }

 

 

No concurrency problems with running results!

Thread 1 operates on the account, and when it is finished, other threads can operate on the account.That is, after each thread has performed a series of operations such as inquiry, withdrawal and deposit on the account, then other threads can operate on the account, ensuring that the balance of the account is no longer problematic.

 

 

 

(2) Use Monitor to solve concurrency problems

Monitor class provides synchronous access to objects

 

The Monitor class controls access to objects by granting them lock to a single thread.

Object locks provide the ability to restrict access to blocks of code, often referred to as critical zones.When a thread owns a lock on an object, no other thread can acquire the lock.You can also use Monitor to ensure that no other thread is allowed access to the application code section being executed by the owner of the lock unless another thread is executing the code using another locked object.

Use Monitor to lock objects (that is, reference types) instead of value types.

Two methods of using Monitor to solve concurrency problems are the TryEnter and Exit methods

 

 

 

The TryEnter method acquires an exclusive lock on the specified object and has several overloaded versions. This is a case with two parameters. The method returns a bool value. If the current thread acquires the lock without blocking it, it returns true; otherwise, it returns false.

The Exit method releases an exclusive lock on the specified object with an object-type parameter.

 

 

 

 

class Program

    {

        public static readonly object _lockObject = new object();

 

        Account acc = new Account("Zhang Lan", "0000", 5000);

 

        static void Main(string[] args)

        {

            Thread[] threads = new Thread[10];

            Program p = new Program();

 

            for (int i = 0; i < 10; i++)

            {

                threads[i] = new Thread(new ThreadStart(p.Run));

                threads[i].Name = "thread" + (i + 1);

            }

 

            foreach (Thread t in threads)

            {

                t.Start();

            }

        }

 

        public void Run()

        {

            ATM atm = new ATM();

            atm.account = acc;

            //Lock quota-related operation codes

            try

            {

                if (Monitor.TryEnter(_lockObject, -1))

                {

 

                    Console.WriteLine(Thread.CurrentThread.Name +

                         ": Query Current" + atm.GetBalance());

 

                    Console.WriteLine(Thread.CurrentThread.Name +

                         ": Draw 2000");

                    //Withdraw money

                    atm.WithDraw(2000);

 

                    Thread.Sleep(100);

 

                    Console.WriteLine(Thread.CurrentThread.Name +

                         ": Query Current" + atm.GetBalance());

 

                    Console.WriteLine(Thread.CurrentThread.Name +

                         ": Deposit 2000");

                    //deposit

                    atm.Deposit(2000);

                    Console.WriteLine(Thread.CurrentThread.Name +

                         ": Query Current" + atm.GetBalance());

 

                }

            }

            finally

            {

                Monitor.Exit(_lockObject);

            }

        }

    }

Posted by trygve on Sat, 03 Aug 2019 15:43:24 -0700