Reputation: 1186
Here i'm doing one process of checking user names. I've created one datagridview and loadded data from the text file. so the datagridview contains first name and last name of users in first two columns . What i need to do is read those values row by row and find that there is no same name for first and last .These operation is performed in separate class. So i need to get result from that class and show the result to gridview alternatively . It all works fine when i'm using just one thread . But when i'm using more than one thread it just throwing an exception that outofbound exception in gridview reading . Here is my coding :
static int i, j=0, l=0,k=0,m=0;
public void check()
{
for (i = 0; i < dataGridView1.Rows.Count ; i++)
{
if (InvokeRequired)
{
Invoke(new UpdateDelegate(delegate
{
if (i == 0 && j==0)
{
DataGridViewColumn col = new DataGridViewTextBoxColumn();
col.HeaderText = "Status";
int colIndex = dataGridView1.Columns.Add(col);
dataGridView1.Rows[i].Cells[colIndex].Value = "Process Started";
j = 1;
}
else
{
dataGridView1.Rows[i].Cells[3].Value = "process Started";
}
}));
}
if (InvokeRequired)
{
Invoke(new UpdateDelegate(delegate
{
Process obj_reg = new Process(dataGridView1.Rows[i].Cells[1].Value.ToString(),dataGridView1.Rows[i].Cells[2].Value.ToString());
string res = obj_reg.register();
Thread.Sleep(500);
if (res.Contains("success"))
{
if (i == 0 && k==0)
{
DataGridViewColumn col = new DataGridViewTextBoxColumn();
col.HeaderText = "Result";
int colIndex = dataGridView1.Columns.Add(col);
dataGridView1.Rows[i].Cells[colIndex].Value = "Ya its different";
dataGridView1.Rows[i].DefaultCellStyle.BackColor = Color.Green;
k = 1;
}
else
{
dataGridView1.Rows[i].Cells[4].Value = "Ya its different";
dataGridView1.Rows[i].DefaultCellStyle.BackColor = Color.Green;
}
}
else
{
if (i == 0 && m == 0)
{
DataGridViewColumn col = new DataGridViewTextBoxColumn();
col.HeaderText = "Result";
int colIndex = dataGridView1.Columns.Add(col);
dataGridView1.Rows[i].Cells[colIndex].Value = "No its same";
dataGridView1.Rows[i].DefaultCellStyle.BackColor = Color.Red;
m = 1;
}
else
{
dataGridView1.Rows[i].Cells[4].Value = "No its same";
dataGridView1.Rows[i].DefaultCellStyle.BackColor = Color.Red;
}
}
}));
}
}
}
public void Button1_Click(Object sender, EventArgs e)
{
Thread[] threads = new Thread[3];
for (int l = 0; l < 3; l++)
{
threads[l] = new Thread(new ThreadStart(check));
}
foreach (Thread t in threads)
{
t.Start();
}
}
Please suggest me how to use Multithread here ... its just an example .. please explain any other way ..
Upvotes: 2
Views: 6246
Reputation: 48949
There are several problems with this code.
You are accessing the DataGridView
from a worker thread. This is evident by the first line of code in check
. You simply cannot do this.
You defined the variables i
, j
, l
, and presumably k
and m
(though I do not see them declared anywhere) as static. The consequence is that each worker thread will be working with the same values and stepping on each other's toes. It is a bigger problem for i
since it actually is being accessed from the worker thread. The others are accessed from anonymous delegate which is marshaled onto the UI thread. Still, that is probably going to cause a lot of confusion because the order in which anonymous delegates are getting executed is unpredictable.
You are calling Invoke
from the worker thread. This is not a problem by itself. But, considering that you are mashaling all of the useful work back onto the UI thread it is rather pointless. Think about it. You went to all that work to use a worker thread and then you marshal everything back onto the UI thread anyway. You now have a solution that is worse than had you not used any worker threads at all.
This is not a problem really, but more of a gripe I have. Why bother calling InvokeRequired
? You already know that "invoking" is required because you know at development time that this stuff is on a worker thread.
My recommendations are as follows.
Do not use threads at all. It is pointless in this case. The only useful work that can be performed on a another thread is the comparison of the first and last names. That is trivial and can be done just as quickly on the UI thread.
If you really want to use a separate thread then you have to read the values from the DataGridView
on the UI thread and put them in a separate data structure; one that is safe to use from another thread. It would be better to maintain this data structure in tandem with the DataGridView
that way when it comes time to initiate your long running operation you will not need to read the grid since you already have the data copied into this separate data structure.
Upvotes: 2
Reputation: 161
Use the lock
statement to prevent different threads to run the same code at the same time. You would need a reference to use as an identifier for the lock. It's common to create a simple object that is used only for the locking:
static object sync = new Object();
lock (sync)
{
// do multithreaded stuff here
}
The UI
is not multithreading friendly so it's not wise to update the DataGridView
directly because it refreshes when a values is being edited. Alter its DataSource
and call for an Update
when all threads finished work.
DataSet dataset = dataGridView1.DataSource;
// do threaded operations on dataset
// update the datagrid when threads finish work
dataGridView1.DataSource = dataset;
dataGridView1.Update();
I noticed in your code runs the same code 3 times instead of splitting your data into 3 parts and updating it independently. Try doing something like this:
threads[l] = new Thread(new ThreadStart(()=>check( startPosition, endPosition));
You could see a different approach using BackgroundThread
here:
Nonblocking update to a DataGridView
Upvotes: 0