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 ..
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.
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
Related
so currently I've got an application that has 2 processes. One process is pining, while pinging the process is writing down the results into an array.
Another process is for updating the UI every second with a timer. Whats being update is an mschart to be more exact.
That's how I have set up the timer:
readonly System.Windows.Forms.Timer myTimer = new System.Windows.Forms.Timer();
myTimer.Interval = 1000;
myTimer.Tick += WriteFunction;
Now this is the method that I'm calling every second for refreshing the UI / actually Graph:
private void WriteFunction(object objectInfo, EventArgs e)
{
foreach (NetPinger.source.AddGraph b in graphList)
{
b.fileRead();
}
}
The method, for updating the chart is inside another class, and looks like this:
public void fileRead()
{
double unixTimestamp = (Int32)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
chart_holder.Series[0].Points.Clear();
for (double i = unixTimestamp; unixTimestamp - graphSizing < i; i--)
{
bool exists;
try
{
exists = Array.Exists(file, element => element.XValue == i);
exists = true;
}
catch
{
exists = false;
}
try
{
if (exists == false)
{
DataPoint point = new DataPoint(i, 0);
chart_holder.Series[0].Points.Add(point);
}
else
{
DataPoint point = Array.Find(file, element => element.XValue == i);
chart_holder.Series[0].Points.Add(point);
}
}
catch(Exception ex)
{
MessageBox.Show(Convert.ToString(ex));
}
}
}
Now what I noticed was that if the graphSizing (number that I'm looping through) is kept low, the performance is kinda fine and everything is sync (multiple graphs from UI are updated at same time etc.) like it should be. But as soon as i rise it let's say to like 50 or even 250 (what the goal should be) the UI and Graph updating are being very very slow. It's only updating like every 3s and the UI is in general very laggy and slow.
Does anyone has any advice how I can maintain good performance or where I messed up that the UI is so slow? For further questions or more details feel free to ask.
Thanks a lot for your time and helping.
Greetings C.User
Your code always runs in the UI thread, since System.Windows.Forms.Timer calls the delegate on the UI thread. Even if that where not the case (and you used System.Timer instead), you delegate everything back to the UI with your Invoke call. You need to make sure you prepare the data on another thread first and do as little as possible in the UI thread itself.
I'm working on a fingerprint and I need to make this operation and I get an error, and don't know how to fix it
private void doVerify(object sender, DoWorkEventArgs args)
{
VerificationResult verificationResult = new VerificationResult();
for (int i = 0; i < lbDatabase.Items.Count || verificationResult.score > 0; i++)
{
lbDatabase.Invoke(new MethodInvoker(delegate { lbDatabase.SelectedItem = i; }));
verificationResult.score = _engine.Verify(((CData)lbDatabase.SelectedItem).EngineUser, 20000, out verificationResult.engineStatus);
}
args.Result = verificationResult;
}
Error: Cross-thread operation not valid: Control 'lbDatabase' accessed
from a thread other than the thread it was created on
You have three places in your code where you access lbDatabase.
The loop - lbDatabase.Items.Count
Setting the selected item - lbDatabase.SelectedItem = i
Retrieving the selected item - lbDatabase.SelectedItem
I assume on the second one you meant to write lbDatabase.SelectedIndex = i.
Only the second one you are invoking. So the other two are running on the background worker thread. That's why you're getting the error.
All access to UI elements - reading and writing - needs to be done on the UI thread.
Now, since you're trying to do this using a background worker you have a method that will freeze up the UI if it solely ran on the UI thread. So you need the parts that access the control to run on the UI, but everything else to run on the background thread.
The next problem is though, that calling lbDatabase.Invoke(new MethodInvoker(delegate { lbDatabase.SelectedItem = i; })); pushes the instruction to the UI, but there is no guarantee that the code will immediately run - in fact the entire loop might run and queue up all of the invokes. You obviously want it to happen synchronously, but that wont happen.
Looking at your code you clearly only want to access all of the CData items on the lbDatabase list box. There's a very easy way to make this work.
Just try this when calling the background worker:
backgroundWorker1.RunWorkerAsync(lbDatabase.Items.Cast<CData>().ToArray());
And then change your doVerify to this:
private void doVerify(object sender, DoWorkEventArgs args)
{
CData[] items = (CData[])args.Argument;
VerificationResult verificationResult = new VerificationResult();
for (int i = 0; i < items.Length || verificationResult.score > 0; i++)
{
verificationResult.score = _engine.Verify(items[i].EngineUser, 20000, out verificationResult.engineStatus);
}
args.Result = verificationResult;
}
The error is pretty self explantory. You're running on a different thread, so you can't interact with the control. This means accessing lbDatabase.Items.Count is out.
If instead you:
lbDatabase.Invoke((MethodInvoker)(() => {
VerificationResult verificationResult = new VerificationResult();
for (int i = 0; i < lbDatabase.Items.Count || verificationResult.score > 0; i++)
{
lbDatabase.SelectedItem = i;
verificationResult.score = _engine.Verify(((CData)lbDatabase.SelectedItem).EngineUser, 20000, out verificationResult.engineStatus);
}
args.Result = verificationResult;
}));
then you'd probably be back in business. Now all of the accesses to the control are queued to run on the UI thread (and you're no longer switching contexts with Invoke in the middle of a loop... costly).
I have the following code that i want to convert to multi threading using c# 4.0. Is it possible to do that? Any guidance is greatly appreciated.
I have a button start start the process and it calls the following function
private void ProcessData()
{
//clear some ui text fields and disable start button and enable cancel button and set status to working
//open database connection
try
{
//populate ui multi line textbox saying that it is getting data from database
var dsResult = new DataSet();
//populate dataset
//populate ui multi line textbox saying that it finished getting data from database
//close connection
if (dsResult.Tables.Count == 1 && dsResult.Tables[0].Rows.Count > 0)
{
//populate another field saying how much records we got
int iCount = 1;
foreach (DataRow dr in dsResult.Tables[0].Rows)
{
if (_stop)
{
//set the status as forced stop
return;
}
//populate the currently processed record count using iCount
//populate ui multi line textbox indicating which item that it is starting to work using dr["Item"]
//call some external function to process some data, inside this function i have to update ui multi line textbox as well
var dataFile = SearchDataFile(dr["Item"].ToString());
if (dataFile == null)
{
//populate ui multi line textbox indicating that item was not found
iCount++;
continue;
}
//call another external function to process some data, inside this function i have to update ui multi line textbox as well
UpdateDataFile(dataFile, folderId, dr, dr["Item"].ToString());
iCount++;
}
}
else
{
//populate ui multi line textbox indicating no data found
}
//update status saying that it is complete
tsslblStatus.Text = "STATUS : COMPLETE";
}
catch (Exception ex)
{
//close connection
//populate ui multi line textbox indicating error occured
//update status to error
}
finally
{
//re adjust ui and enabling start and disable stop
//set _stop variable to false
}
}
Thanks
First split your logic
SomeJobA();
SomeJobB();
SomeJobC();
...
then do some multi-threading
start SomeJobA() thread/task
start SomeJobB() thread/task
start SomeJobC() thread/task
...
to wait or not to wait for them to finish?
To update UI from other thread use Invoke / BeginInvoke.
The easiest way I've found is to use the Parallel.ForEach method, so instead of
foreach (DataRow dr in dsResult.Tables[0].Rows)
use
Parellel.Foreach(dsResult.Tables[0].Rows, dr =>
{
//foreach body code goes here.
});
However, if you are trying to update an algorithm that manipulates the UI to take advantage of concurrency you are going to have a bad time. Win form applications (and if I recall correctly, Win 8 / phone apps) do not allow you to manipulate the UI (ie write to a textbox) from any thread other than the main thread.
In order for you to parallelize this algorithm properly, you'll need to separate out all of the code that manipulates the UI.
You can use the TaskFactory to marshal out the work you want to do in parallel:
public class MyState{
public string Example {get;set;}
}
private MyState _state;
private void MethodCalledFromUIThread()
{
//Update UI.
TextBox1.Text = string.Empty;
//Start parallel work in a new thread.
new TaskFactory().StartNew(() => ThreadedMethod())
//Wait for background threads to complete
.Wait();
//Update UI with result of processing.
TextBox1.Text = _state.Example;
}
private void ThreadedMethod()
{
//load dsResult
Parallel.ForEach(dsResult.Tables[0].Rows, dr =>
{
//process data in parallel.
}
//Update the State object so the UI thread can get access to the data
_state = new MyState{Example = "Data Updated!";}
}
I am writing an application with two parts, one part downloads data and lists its sources in a file which is being monitored by the other part which, every 15 minutes when the data is downloaded therefore updating the file, it loads the file contents and removes the old data. I currently have this code:
private void FileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
{
try
{
fsw.EnableRaisingEvents = false;
MessageBox.Show("File Changed: " + e.FullPath);
_times.Clear();
XmlDocument dataset = new XmlDocument();
dataset.Load(#"C:\Users\Henry\AppData\Local\{9EC23EFD-F1A4-4f85-B9E9-729CDE4EF4C7}\cache\DATA_RAINOBS\dataset.xml");
for (int x = 0; x < dataset.SelectNodes("//Times/Time").Count; x++)
{
_times.Add(
new Time()
{
Original = dataset.SelectNodes("//Times/Time/#original")[x].InnerText,
Display = dataset.SelectNodes("//Times/Time/#display")[x].InnerText,
Directory = dataset.SelectNodes("//Times/Time/#directory")[x].InnerText + "_LORES.png"
});
}
times.SelectedIndex = 0;
}
finally { fsw.EnableRaisingEvents = true; }
}
But when I run it, I get a System.NotSupportedException and from further information I know that it is because I am trying to manipulate a list from a separate thread created by the FileSystemWatcher.
I have literally done hardly any programming using multiple threads so I have no idea what to do. It would be very helpful if someone could modify the code above so that it is thread safe and will work properly because then I will have something to learn from and it won't be wrong. How do I make this work?
You have to use Invoke on the control (or its owner). The method will then be queued for processing on the control's thread, rather than the FSW's thread.
On WPF, this is handled by a dispatcher for this, eg.
times.Dispatcher.Invoke(() => { yourCode; });
If you're expecting your code to take some time, you might consider only doing the invoke with a full list of items at the end, rather than invoking the whole operation.
Your _times collection is binded with GUI part so error must be at line
_times.Clear();
WPF has constraint that you cannot modify source collection which is binded to GUI from thread other than UI thread. Refer to my answer over here for details.
As stated above you can modify it from only UI thread so consider dispatching this stuff on UI dispatcher which will queue this on UI thread. Get UI dispatcher like this:
App.Current.Dispatcher.Invoke((Action)delegate
{
_times.Clear();
});
Also make sure any subsequent calls related to UI is dispatched on UI thread. May be wrap it under one call only:
XmlDocument dataset = new XmlDocument();
dataset.Load(#"C:\Users\Henry\AppData\Local\{9EC23EFD-F1A4-4f85-B9E9-
729CDE4EF4C7}\cache\DATA_RAINOBS\dataset.xml");
App.Current.Dispatcher.Invoke((Action)delegate
{
_times.Clear();
for (int x = 0; x < dataset.SelectNodes("//Times/Time").Count; x++)
{
_times.Add(
new Time()
{
Original = dataset.SelectNodes("//Times/Time/#original")
[x].InnerText,
Display = dataset.SelectNodes("//Times/Time/#display")
[x].InnerText,
Directory = dataset.SelectNodes("//Times/Time/#directory")
[x].InnerText + "_LORES.png"
});
}
_times.SelectedIndex = 0;
});
I'm running the following code to start my threads, but they don't start as intended. For some reason, some of the threads start with the same objects (and some don't even start). If I try to debug, they start just fine (extra delay added by me clicking F10 to step through the code).
These are the functions in my forms app:
private void startWorkerThreads()
{
int numThreads = config.getAllItems().Count;
int i = 0;
foreach (ConfigurationItem tmpItem in config.getAllItems())
{
i++;
var t = new Thread(() => WorkerThread(tmpItem, i));
t.Start();
//return t;
}
}
private void WorkerThread(ConfigurationItem cfgItem, int mul)
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(10*mul);
}
this.Invoke((ThreadStart)delegate()
{
this.textBox1.Text += "Thread " + cfgItem.name + " Complete!\r\n";
this.textBox1.SelectionStart = textBox1.Text.Length;
this.textBox1.ScrollToCaret();
});
}
Anyone able to help me out?
Starting a thread doesn't really start the thread. Instead it schedules it for execution. I.e. at some point it will get to run when it is scheduled. Scheduling threads is a complex topic and an implementation detail of the OS, so your code should not expect a certain scheduling.
You're also capturing variables in your lambda. Please see this post (there is a section on Captured Variables) for the problems associated with doing that.
You just run into the (be me called) lambda error.
You provide the ConfigurationItem from the foreach loop directly. This leads to the fact, that all your threads get the same item (the last one).
To get this to work you have to create a reference for each item and apply this to each thread:
foreach (ConfigurationItem tmpItem in config.getAllItems())
{
i++;
var currentI = i;
var currentItem = tmpItem;
var t = new Thread(() => WorkerThread(currentItem, currentI));
t.Start();
//return t;
}
And you should also consider using a ThreadPool.
MSDN Description about how to use the ThreadPool
Short summary of differences here on SO
The problem seems to be there : () => WorkerThread(tmpItem, i)
I'm not used to Func<> but it seems to work like anonymous delegates in .NET 2.0. Thus, you may have a reference to the arguments of the WorkerThread() method. Hence, their values are retrieved later (when the thread actually runs).
In this case, you may already be at the next iteration of your main thread...
Try this instead :
var t = new Thread(new ParametrizedThreadStart(WorkerThread));
t.Start(new { ConfigurationItem = tmpItem, Index = i } );
[EDIT] Other implementation. More flexible if you need to pass new parameters to the thread in the future.
private void startWorkerThreads()
{
int numThreads = config.getAllItems().Count;
int i = 0;
foreach (ConfigurationItem tmpItem in config.getAllItems())
{
i++;
var wt = new WorkerThread(tmpItem, i);
wt.Start();
//return t;
}
}
private class WorkerThread
{
private ConfigurationItem _cfgItem;
private int _mul;
private Thread _thread;
public WorkerThread(ConfigurationItem cfgItem, int mul) {
_cfgItem = cfgItem;
_mul = mul;
}
public void Start()
{
_thread = new Thread(Run);
_thread.Start();
}
private void Run()
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(10 * _mul);
}
this.Invoke((ThreadStart)delegate()
{
this.textBox1.Text += "Thread " + _cfgItem.name + " Complete!\r\n";
this.textBox1.SelectionStart = textBox1.Text.Length;
this.textBox1.ScrollToCaret();
});
}
}
Do you really need to spawn threads manually (which is a rather expensive task) ? You could try to switch to the ThreadPool instead.
You can't assume that the threads will run in the same order they were called, unless you force it, and cause a dependency between them.
So the real question is - what is your goal ?
I think that the error is somewhere else. Here are some hints to help you debug :
Give a name containing to each thread, and display the thread name instead of the config item name :
this.textBox1.Text += "Thread " + Thread.Current.Name + " Complete!\r\n";
Display the content of config.getAllItems(), may be that some items has the same name (duplicated)
===========
Here are some additional information about multi threading with winforms:
dont create new Thread directly, use the ThreadPool instead :
ThreadPool.QueueUserWorkItem(state => { WorkerThread(tmpItem, i); });
If you really want to creat your threads, use this.BeginInvoke instead of this.Invoke your worker thread will finish sooner => less concurrent thread => better global performance
don't call Thread.Sleep in a loop, just do a big sleep: Thread.Sleep(10*mul*100);
I hope that this will help you.
Thanks to all of you!
I just implemented the threadpool, and that worked like a charm - with the added bonus of not spawning too many threads at once.
I'll have a look at the other solutions, too, but this time around the threadpool will save me from having to manually check for bozos with too many configs ;)