Thursday, February 28, 2013

.Net WinForm Based Producer consumer Thread app

Today  i gave a interview for one of investment bank firm , It was supposed to be technical oral interview but they also conducted a machine test for me to implement Producer consumer Queue problem in Winform using dot net threading . I know this is a very common example and know to most of you , but i just want to share my implementation which i tried without help of google :D , ( i did not had internet access in test machine).

Problem statement was.
-> we have two xml files containing  products ( collection of product - ProdID,ProdName,Descprtion ) .
->We should create two threads to read the data from each file respectively and insert them into a queue parallel which is nothing but a  List<Product> queue , while inserting record we also need avoid inserting  duplicate records .
-> and also consumer thread should read the data from queue parallel and insert the newly added rows  into datagridview .

Solution i gave (since i dont have those 2 xml files now i using random number generator to create products for this blog post) :

Logic :
-> Created two producer background thread and one consumer thread, and a queue List<product>
-> used queue object as lock , so when the threads need to perform their operation on queue they need to acquire the lock on the queue object to perform the insert/read operation on queue.
                  public void AddToQueue(Product product , string ThreadName)
        {
            lock (queue)
            {
                textBox3.Invoke((MethodInvoker)delegate
                {
                    textBox3.Text = ThreadName;
                });
                if (!queue.Any(a => a.ProdId == product.ProdId))
                {
                    queue.Add(product);
                }
                Thread.Sleep(50);
            }
        }
-> and to perform the cross thread operation on UI thread used below  code

                    dataGridView1.Invoke((MethodInvoker)delegate
                    {
                        foreach (var item in queue.FindAll(a => !a.IsInserted))
                        {
                            DataRow row = source.NewRow();

                            row.ItemArray = new object[] {dataGridView1.RowCount, item.ProdId,item.Name,item.ThreadName };
                            source.Rows.Add(row);
                            item.IsInserted = true;
                        }
                        Thread.Sleep(50);
                    });

used thread.sleep to give some delay .

-> attached the screen of the UI and also please find source code for same below.

please find whole app in link 




namespace ProducerConsumer
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            //Creating 2 producer and 1 consumer thread

            producer1 = new BackgroundWorker();
            producer2 = new BackgroundWorker();
            consuemer = new BackgroundWorker();

            producer1.DoWork += producer1_DoWork;
            producer2.DoWork += producer2_DoWork;
            consuemer.DoWork += consuemer_DoWork;

            //Define Table for DataGrid
            DefineDataTable();
        }

        BackgroundWorker producer1;
        BackgroundWorker producer2;
        BackgroundWorker consuemer;

       // Queue
        List<Product> queue = new List<Product>();

        bool IsThread1Complete;
        bool IsThread2Complete;

        void consuemer_DoWork(object sender, DoWorkEventArgs e)
        {
            Consumer();
        }

        void producer2_DoWork(object sender, DoWorkEventArgs e)
        {
            AdditionByProducer2();
        }

        void producer1_DoWork(object sender, DoWorkEventArgs e)
        {
            AdditionByProducer1();
        }

        DataTable source = new DataTable();
        public void DefineDataTable()
        {
            source.Columns.Add("SNo", typeof(int));
            source.Columns.Add("ProductID",typeof(int));
            source.Columns.Add("ProductName",typeof(string));
            source.Columns.Add("ThreadNAme", typeof(string));
            dataGridView1.DataSource = source;

            dataGridView1.Columns[0].DataPropertyName = "SNo";

            dataGridView1.Columns[1].DataPropertyName = "ProductID";
            dataGridView1.Columns[2].DataPropertyName = "ProductName";
            dataGridView1.Columns[3].DataPropertyName = "ThreadNAme";
            dataGridView1.AutoGenerateColumns = false;
        }

     

        public void AdditionByProducer1()
        {
            for (int i = 0; i < 200; i++)
            {
                AddToQueue(Product.GetProduct("Producer One"), "Producer One");
                textBox1.Invoke((MethodInvoker)delegate
                {
                    textBox1.Text = "200 / " + i.ToString();
                });

            }
            IsThread1Complete = true;
        }

        public void AdditionByProducer2()
        {
            for (int i = 0; i < 200; i++)
            {
                AddToQueue(Product.GetProduct("Producer Two"), "Producer Two");

                textBox2.Invoke((MethodInvoker)delegate
                {
                    textBox2.Text = "200 / " + i.ToString();
                });
            }
            IsThread2Complete = true;
        }

        public void AddToQueue(Product product , string ThreadName)
        {
            lock (queue)
            {
                textBox3.Invoke((MethodInvoker)delegate
                {
                    textBox3.Text = ThreadName;
                });
                if (!queue.Any(a => a.ProdId == product.ProdId))
                {
                    queue.Add(product);
                }
                Thread.Sleep(50);
            }
        }

        public void Consumer()
        {
            while (!IsThread1Complete || !IsThread2Complete)
            {
                lock (queue)
                {
                    textBox3.Invoke((MethodInvoker)delegate
                    {
                        textBox3.Text = "Consumer ";
                    });

                    dataGridView1.Invoke((MethodInvoker)delegate
                    {
                        foreach (var item in queue.FindAll(a => !a.IsInserted))
                        {
                            DataRow row = source.NewRow();

                            row.ItemArray = new object[] {dataGridView1.RowCount, item.ProdId,item.Name,item.ThreadName };
                            source.Rows.Add(row);
                            item.IsInserted = true;
                        }
                        Thread.Sleep(50);
                    });
                }
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            producer1.RunWorkerAsync();
            producer2.RunWorkerAsync();
            consuemer.RunWorkerAsync();
        }

    }


    public class Product
    {
        public int ProdId { get; set; }
        public string Name { get; set; }
        public string ThreadName { get; set; }
        public bool IsInserted { get; set; }

        public static Product GetProduct(string tName)
        {
            Random rand = new Random();
            int id = rand.Next(1, 500);

            return new Product { ProdId = id, Name = "Prodcut" + id.ToString(), ThreadName = tName };
        }
    }
}





1 comment:

  1. Nice article Post some more interview questions & problems for beginners like me :p

    ReplyDelete