Monday, April 20, 2009

Threading

A statement is atomic if it executes as a single indivisible instruction.In a 32 bit cpu reading and writing a less than(or equal) 32 bit field is atomic,which can be executed in one instruction,

Reading or writing a 64 bit field is non atomic,the cpu writes the first 32 bit memory and then the second 32 bit memory,if a program reads the first 32 bit while another is writing the second,there can be a bitwise combination of the new and old values.

Lets try if it happens,

public partial class Form1 : Form
    {
        delegate void SetTextDelegate(string text);
    
 
 
        Int64 number =100000000;
        public Form1()
        {
            InitializeComponent();
        }
 
        private void AdRichTextBox(string value)
        {
            richTextBox1.Text += value + "\n";
        }
 
        private void Form1_Load(object sender, EventArgs e)
        {
            Thread j = new Thread(Jam);
            j.Start();
            for (int i = 0; i < 10; i++)
            {
                Thread t = new Thread(Go);
                t.Name = "thread" + i.ToString();
                t.Start();
                richTextBox1.Text += "thread" + i.ToString() + "started" + "\n";
            }
            richTextBox1.Text += number.ToString();
            
 
        }
    
        public void Go()
        {
            SetTextDelegate spd = new SetTextDelegate(AdRichTextBox);
            
                for (int cycles = 0; cycles < 100; cycles++)
                {
                    number-- ;
                     this.Invoke(spd, new object[] { Thread.CurrentThread.Name + " " + number.ToString()});
                    
                }
                doJam = false;
            
        }
        public bool doJam = true;
        public void Jam()
        {
            while (doJam)
            {
                Int64 temp = number;
                number = 0;
                number = temp;
            }
        }
 
    }

this is a basic demo Jam method reads the number writes it to a buffer assignes 0 and rewrites buffet to the number, when you run the code we see while the jam writes 0 the go reads the number so it fails.

So here is another example which is famous and good so i will stick with it

Think you write a banking program or an atm program if the same user acceses his account from multiple places and wants to withdraw some money from multiple clients at the same time the first block reads the initial value of bank account the second reads the first takes all money the second thinks money is there the second takes all money too. Lets see in action :

class Account
    {
        int balance;
 
        Random r = new Random();
 
        public Account(int initial)
        {
            balance = initial;
        }
 
        int Withdraw(int amount)
        {
 
            if (balance < 0)
            {
                Console.WriteLine("Negative");
            }
 
 
            lock (this)
            {
            if (balance >= amount)
            {
                Console.WriteLine("Balance before Withdrawal :  " + balance);
                Console.WriteLine("Amount to Withdraw        : -" + amount);
                balance = balance - amount;
                Console.WriteLine("Balance after Withdrawal  :  " + balance);
                return amount;
            }
            else
            {
                Console.WriteLine("Not Enough Credit");
                return 0; // transaction rejected
            }
            }
        }
 
        public void DoTransactions()
        {
            for (int i = 0; i < 20; i++)
            {
                Withdraw(r.Next(10, 1000));
            }
        }
    }
 
    class Test
    {
        public static void Main()
        {
            Thread[] threads = new Thread[10];
            Account acc = new Account(1000);
            for (int i = 0; i < 10; i++)
            {
                Thread t = new Thread(new ThreadStart(acc.DoTransactions));
                threads[i] = t;
            }
            for (int i = 0; i < 10; i++)
            {
                threads[i].Start();
            }
            Console.Read();
        }
    }

if you comment the lock statement you can see how the balance drops to negative values.

No comments: