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!";}
}
Related
Following is a method that does Insert into the database table. And I am calling this method within DoWork() of BackGroundWorker thread. It obviously throws me the "cross thread operation not valid..." error. As I understand, I could use Invoke() method on UI controls if those to be accessed within DoWork(). BUT does it mean each of the following UI controls should have to be invoked? Is there a better way to achieve this?
private void AddToOccupations()
{
if (dataGridViewSearch.SelectedRows.Count > 0)
{
foreach (DataGridViewRow datarow in dataGridViewSearch.SelectedRows)
{
try
{
AllocationModel occupation = new AllocationModel()
{
User_ID = userID,
Merge_Status = (int)((MergeStatus)Enum.Parse(typeof(MergeStatus), cmbMergeStatus.SelectedItem.ToString())),
Start_Date = dateTimePickerFROMDate.Value,
Seat_Type = datarow.Cells[2].Value.ToString(),
Occupation_Status = cmbStatus_Type.SelectedItem.ToString(),
Session = datarow.Cells[3].Value.ToString(),
Seat_Number = (Int32)datarow.Cells[0].Value,
Number_of_Guests = (Int32)datarow.Cells[1].Value
};
// Call the service method
var success = this.allocationService.AddToOccupation(occupation);
if (success)
{
// display the message box
MessageBox.Show(
Resources.Add_Occupation_Success_Message,
Resources.Add_Occupation_Success_Title,
MessageBoxButtons.OK,
MessageBoxIcon.Information);
// Reset the screen
//this.Reset();
DoCurrentFreeSearch();
}
else
MessageBox.Show("No Records Inserted!");
}
catch (FormatException ex)
{
errorMessage = "Insert Error: \n";
errorMessage += ex.Message;
MessageBox.Show(errorMessage);
}
}
}
else
MessageBox.Show("Warning! No Row is selected...");
}
You should separate the worker code from the GUI code. Don't you have a DataSource on that data grid? You should basically first get the data you need from the grid (the selected rows) and pass them to the background worker from the GUI code (the button click or whatever). You can then report the work progress through BackgroundWorker.ReportProgress (which executes the progress event on the GUI thread).
If you can't decouple the GUI code from the worker code completely, you'll have to use Invokes for the rest. However, it's not clear from your code why you would need that, ReportProgress should be quite enough.
The general answer to your title question is: Yes.
If a background worker thread needs to access a UI item it can only do so by Invoking a method of it.
The problem of putting data into a DGV from a BW is best addressed by accessing its DataSource in a de-coupled way. I just did a little test to see if my suggestion from the original post works and it looks fine.
So, like with the Bitmap, you can create two DataSources as Properties; one to fill from the background worker and one to use as the DGV's Datasource.
public List<SeatData> theSeats_buffer { get; set; }
public List<SeatData> theSeats_DS { get; set; }
In the BW thread DoWork() you fill the theSeats_buffer list by calling an appropriate function void or bool getSeatData() and when you are done with the workload you pass the data into the theSeats_DS, maybe like this:
theSeats_DS= new List<Cols>(); theSeats_DS.AddRange(theSeats_buffer);
Again, this operation must be made thread-safe, probably by locking the receiving list theSeats_DS.
since the DataSource has been re-created it should be re-assigned in the bw_RunWorkerCompleted event; I did it right along with invalidating the display panel1:
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ((e.Cancelled == true))
{ this.tbProgress.Text += "Cancelled!"; }
else if (!(e.Error == null))
{ this.tbProgress.Text += ("Error: " + e.Error.Message); }
else
{ panel1.Invalidate(); DGV.DataSource = theSeats_DS; }
}
With regard to the DB Inserts, I don't know how it relates. This answer is only about getting data from somewhere asynchrously and sending them to the UI.
Getting data from the DGV into the database is not something that would happen in the BW thread though, at least not the one that gets triggered upon incoming changes. If you use the DGV for input, concurrency will be an issue!! It is bad enough, if the seat one tries to reserve is actually taken by the time you press enter. But that can't be prevented. However, you need to ensure that the input isn't wiped out by incoming changes.. OTOH, a signal would be nice..
Concurrency is tricky!
Since the controls run on the UI thread, and not on the background worker thread, all of their functions need to be invoked.
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;
});
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
I am trying to populate a text box with some data, namely the names of several instruments a line at a time.
I have a class that will generate and return a list of instruments, I then iterate through the list and append a new line to the text box after each iteration.
Starting the Thread:
private void buttonListInstruments_Click(object sender, EventArgs e)
{
if (ins == null)
{
ins = new Thread(GetListOfInstruments);
ins.Start();
}
else if (ins != null)
{
textBoxLog.AppendText("Instruments still updating..");
}
}
Delegate to update textbox:
public delegate void UpdateLogWithInstrumentsCallback(List<Instrument> instruments);
private void UpdateInstruments(List<Instrument> instruments)
{
textBoxLog.AppendText("Listing available Instruments...\n");
foreach (var value in instruments)
{
textBoxLog.AppendText(value.ToString() + "\n");
}
textBoxLog.AppendText("End of list. \n");
ins = null;
}
Invoking the control:
private void GetListOfInstruments()
{
textBoxLog.Invoke(new UpdateLogWithInstrumentsCallback(this.UpdateInstruments),
new object[] { midiInstance.GetInstruments() });
}
Note: GetInstruments() returns a List of type Instrument.
I am implementing therads to try to keep the GUI functional whilst the text box updates.
For some reason the other UI controls on the WinForm such as a seperate combo box remain inactive when pressed until the text box has finished updating.
Am I using threads correctly?
Thanks.
You haven't accomplished anything, the UpdateInstruments() method still runs on the UI thread, just like it did before. Not so sure why you see such a long delay, that must be a large number of instruments. You can possibly make it is less slow by first appending all of them into a StringBuilder, then append its ToString() value to the TextBox. That cuts out the fairly expensive Windows call.
I would recommend using a SynchronizationContext in general:
From the UI thread, e.g. initialization:
// make sure a SC is created automatically
Forms.WindowsFormsSynchronizationContext.AutoInstall = true;
// a control needs to exist prior to getting the SC for WinForms
// (any control will do)
var syncControl = new Forms.Control();
syncControl.CreateControl();
SyncrhonizationContext winformsContext = System.Threading.SynchronizationContext.Current;
Later on, from any thread wishing to post to the above SC:
// later on -- no need to worry about Invoke/BeginInvoke! Whoo!
// Post will run async and will guarantee a post to the UI message queue
// that is, this returns immediately
// it is OKAY to call this from the UI thread or a non-UI thread
winformsContext.Post(((state) => ..., someState);
As others have pointed out, either make the UI update action quicker (this is the better method!!!) or separate it into multiple actions posted to the UI queue (if you post into the queue then other message in the queue won't be blocked). Here is an example of "chunking" the operations into little bit of time until it's all done -- it assumes UpdateStuff is called after the data is collected and not necessarily suitable when the collection itself takes noticeable time. This doesn't take "stopping" into account and is sort of messy as it uses a closure instead of passing the state. Anyway, enjoy.
void UpdateStuff (List<string> _stuff) {
var stuff = new Queue<string>(_stuff); // make copy
SendOrPostCallback fn = null; // silly so we can access in closure
fn = (_state) => {
// this is in UI thread
Stopwatch s = new Stopwatch();
s.Start();
while (s.ElapsedMilliseconds < 20 && stuff.Count > 0) {
var item = stuff.Dequeue();
// do stuff with item
}
if (stuff.Count > 0) {
// have more stuff. we may have run out of our "time-slice"
winformsContext.Post(fn, null);
}
};
winformsContext.Post(fn, null);
}
Happy coding.
Change this line:
textBoxLog.Invoke(new UpdateLogWithInstrumentsCallback(this.UpdateInstruments),
new object[] { midiInstance.GetInstruments() });
with this:
textBoxLog.BeginInvoke(new UpdateLogWithInstrumentsCallback(this.UpdateInstruments),
new object[] { midiInstance.GetInstruments() });
You are feeding all instruments into the textbox at once rather then one-by-one in terms of threading. The call to Invoke shall be placed in the for-loop and not to surround it.
nope, you start a thread, and then use invoke, which basically means you are going back to the UI thread to do the work... so your thread does nothing!
You might find that it's more efficient to build a string first and append to the textbox in one chunk, instead of line-by-line. The string concatenation operation could then be done on the helper thread as well.
many time we populate UI with data from DB in the form load and that is why form gets freeze for few second. so i just want to know how can i load data asynchronously and populate UI in form load as a result my form will not freeze and also will be responsive but i don't want to use background worker class. please help me with sample code which can solve my problem.
thanks
Here is a well commented example code:
Example:
// This method can be called on Form_Load, Button_Click, etc.
private void LoadData()
{
// Start a thread to load data asynchronously.
Thread loadDataThread = new Thread(LoadDataAsync);
loadDataThread.Start();
}
// This method is called asynchronously
private void LoadDataAsync()
{
DataSet ds = new DataSet();
// ... get data from connection
// Since this method is executed from another thread than the main thread (AKA UI thread),
// Any logic that tried to manipulate UI from this thread, must go into BeginInvoke() call.
// By using BeginInvoke() we state that this code needs to be executed on UI thread.
// Check if this code is executed on some other thread than UI thread
if (InvokeRequired) // In this example, this will return `true`.
{
BeginInvoke(new Action(() =>
{
PopulateUI(ds);
}));
}
}
private void PopulateUI(DataSet ds)
{
// Populate UI Controls with data from DataSet ds.
}
Command.BeginExecuteReader()
may serves your need for reading purposes.
Here is Sample Code for this method.
You can call Application.DoEvents() while waiting for response to keep your window responsive.
Have a look at this article. http://aspadvice.com/blogs/azamsharp/archive/2007/04/05/Executing-a-Query-Asynchronously-in-.NET-2.0.aspx
It still uses Background worker. Honestly, I can't think of an alternative solution to this this other than threading your application to execute queries and bind returned results. If you do decide to use threads, than i suggest you take a look at this article about thread-pooling for asynchronous execution: http://www.yoda.arachsys.com/csharp/threads/threadpool.shtml
Your best course of action is to use another thread. You can use one straight from the thread pool by calling ThreadPool.QueueUserWorkItem.
private void OnFormLoad()
{
ThreadPool.QueueUserWorkItem(() => GetSqlData());
}
private object GetSqlData()
{
using (var connection = new SqlCeConnection("ConnectionString"))
{
using(var command = new SqlCeCommand())
{
command.Connection = connection;
command.CommandText = "SELECT * FROM tbl_hello";
command.ExecuteReader();
while (command.ExecuteReader().Read())
{
//Put data somewhere
}
}
}
}