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

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