How to make thread safe calls from FileSystemWatcher - c#

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

Related

How to don't wait the function while it's working

I have function
void Search(string text) { ... }
Inside there are many SQLite queries like
List<Word> words = Database.connection.Table<Word>().Where(x => x.word == text).ToListAsync().Result;
And it's needed some time to complete this (about 3 sec).
At this moment interface is freezed.
It's not good. How to solve this problem and don't wait this function?
The bulk of your time is this bit of code
ToListAsync().Result
The LINQ statement itself does not do any work
xxx.Where(x => x.word == text)
until you request items from it. This can be either from a foreach statement, or a ToList statement. When you call ToListAsync you are requesting the work to be done asynchronously, which means that it will not tie up the UI thread. But then the .Result makes it synchronous. So, you are not taking advantage of the threading that is already provided. If you change the signature of the method and how you are getting the results, you'll be able to offload the work.
private async Task Search(string text)
{
// execute the LINQ query with the TPL
List<Word> words = await Database.connection.Table<Word>().Where(x => x.word == text).ToListAsync();
// we are back on the UI thread
foreach(Word word in words)
{
// do something
}
}
There is no need to create a BackgroundWorker or make your code more complex. If you are developing for Windows Phone 7, you'll want to add the Microsoft.Bcl.Async nuget package to your app.
A BackgroundWorker is a good class for a novice at using threads. It has a simple interface and can even provide feedback to feed a ProgressBar in the UI as the work progresses. You can find full details with examples in the How to use a background worker for Windows Phone page on MSDN. An example from the linked page:
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 1; i <= 10; i++)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
// Perform a time consuming operation and report progress.
System.Threading.Thread.Sleep(500);
worker.ReportProgress(i * 10);
}
}
}
This is the method that gets run on the background thread. You can see that you even have the possibility to cancel the long running process at any time. The ReportProgress method is used to pass back a value representing the amount of work done.

Multithreading with Datagridview C#

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

Parallel.ForEach freezing on last loop [duplicate]

More newbie questions:
This code grabs a number of proxies from the list in the main window (I couldn't figure out how to make variables be available between different functions) and does a check on each one (simple httpwebrequest) and then adds them to a list called finishedProxies.
For some reason when I press the start button, the whole program hangs up. I was under the impression that Parallel creates separate threads for each action leaving the UI thread alone so that it's responsive?
private void start_Click(object sender, RoutedEventArgs e)
{
// Populate a list of proxies
List<string> proxies = new List<string>();
List<string> finishedProxies = new List<string>();
foreach (string proxy in proxiesList.Items)
{
proxies.Add(proxy);
}
Parallel.ForEach<string>(proxies, (i) =>
{
string checkResult;
checkResult = checkProxy(i);
finishedProxies.Add(checkResult);
// update ui
/*
status.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Normal,
new Action(
delegate()
{
status.Content = "hello" + checkResult;
}
)); */
// update ui finished
//Console.WriteLine("[{0}] F({1}) = {2}", Thread.CurrentThread.Name, i, CalculateFibonacciNumber(i));
});
}
I've tried using the code that's commented out to make changes to the UI inside the Parallel.Foreach and it makes the program freeze after the start button is pressed. It's worked for me before but I used Thread class.
How can I update the UI from inside the Parallel.Foreach and how do I make Parallel.Foreach work so that it doesn't make the UI freeze up while it's working?
Here's the whole code.
You must not start the parallel processing in your UI thread. See the example under the "Avoid Executing Parallel Loops on the UI Thread" header in this page.
Update: Or, you can simply create a new thread manuall and start the processing inside that as I see you have done. There's nothing wrong with that too.
Also, as Jim Mischel points out, you are accessing the lists from multiple threads at the same time, so there are race conditions there. Either substitute ConcurrentBag for List, or wrap the lists inside a lock statement each time you access them.
A good way to circumvent the problems of not being able to write to the UI thread when using Parallel statements is to use the Task Factory and delegates, see the following code, I used this to iterate over a series of files in a directory, and process them in a Parallel.ForEach loop, after each file is processed the UI thread is signaled and updated:
var files = GetFiles(directoryToScan);
tokenSource = new CancellationTokenSource();
CancellationToken ct = tokenSource.Token;
Task task = Task.Factory.StartNew(delegate
{
// Were we already canceled?
ct.ThrowIfCancellationRequested();
Parallel.ForEach(files, currentFile =>
{
// Poll on this property if you have to do
// other cleanup before throwing.
if (ct.IsCancellationRequested)
{
// Clean up here, then...
ct.ThrowIfCancellationRequested();
}
ProcessFile(directoryToScan, currentFile, directoryToOutput);
// Update calling thread's UI
BeginInvoke((Action)(() =>
{
WriteProgress(currentFile);
}));
});
}, tokenSource.Token); // Pass same token to StartNew.
task.ContinueWith((t) =>
BeginInvoke((Action)(() =>
{
SignalCompletion(sw);
}))
);
And the methods that do the actual UI changes:
void WriteProgress(string fileName)
{
progressBar.Visible = true;
lblResizeProgressAmount.Visible = true;
lblResizeProgress.Visible = true;
progressBar.Value += 1;
Interlocked.Increment(ref counter);
lblResizeProgressAmount.Text = counter.ToString();
ListViewItem lvi = new ListViewItem(fileName);
listView1.Items.Add(lvi);
listView1.FullRowSelect = true;
}
private void SignalCompletion(Stopwatch sw)
{
sw.Stop();
if (tokenSource.IsCancellationRequested)
{
InitializeFields();
lblFinished.Visible = true;
lblFinished.Text = String.Format("Processing was cancelled after {0}", sw.Elapsed.ToString());
}
else
{
lblFinished.Visible = true;
if (counter > 0)
{
lblFinished.Text = String.Format("Resized {0} images in {1}", counter, sw.Elapsed.ToString());
}
else
{
lblFinished.Text = "Nothing to resize";
}
}
}
Hope this helps!
If anyone's curious, I kinda figured it out but I'm not sure if that's good programming or any way to deal with the issue.
I created a new thread like so:
Thread t = new Thread(do_checks);
t.Start();
and put away all of the parallel stuff inside of do_checks().
Seems to be doing okay.
One problem with your code is that you're calling FinishedProxies.Add from multiple threads concurrently. That's going to cause a problem because List<T> isn't thread-safe. You'll need to protect it with a lock or some other synchronization primitive, or use a concurrent collection.
Whether that causes the UI lockup, I don't know. Without more information, it's hard to say. If the proxies list is very long and checkProxy doesn't take long to execute, then your tasks will all queue up behind that Invoke call. That's going to cause a whole bunch of pending UI updates. That will lock up the UI because the UI thread is busy servicing those queued requests.
This is what I think might be happening in your code-base.
Normal Scenario: You click on button. Do not use Parallel.Foreach loop. Use Dispatcher class and push the code to run on separate thread in background. Once the background thread is done processing, it will invoke the main UI thread for updating the UI. In this scenario, the background thread(invoked via Dispatcher) knows about the main UI thread, which it needs to callback. Or simply said the main UI thread has its own identity.
Using Parallel.Foreach loop: Once you invoke Paralle.Foreach loop, the framework uses the threadpool thread. ThreadPool threads are chosen randomly and the executing code should never make any assumption on the identity of the chosen thread. In the original code its very much possible that dispatcher thread invoked via Parallel.Foreach loop is not able to figure out the thread which it is associated with. When you use explicit thread, then it works fine because the explicit thread has its own identity which can be relied upon by the executing code.
Ideally if your main concern is all about keeping UI responsive, then you should first use the Dispatcher class to push the code in background thread and then in there use what ever logic you want to speedup the overall execution.
if you want to use parallel foreach in GUI control like button click etc
then put parallel foreach in Task.Factory.StartNew
like
private void start_Click(object sender, EventArgs e)
{
await Task.Factory.StartNew(() =>
Parallel.ForEach(YourArrayList, (ArraySingleValue) =>
{
Console.WriteLine("your background process code goes here for:"+ArraySingleValue);
})
);
}//func end
it will resolve freeze/stuck or hang issue

How to use a BackgroundWorker to update multiple labels?

This is a follow up question to Updating a dialog from another form (The code and screenshots can be found there)
To solve my GUI hanging problem I received 2 recommendations:
Using Application.DoEvents()
Using a BackgroundWorker
The DoEvents() approach works, however it has been pointed out that I should not use it. Indeed, I notice that the GUI updates correctly but is unresponsive for short times.
That's why I want to use a BackgroundWorker and have read up on it.
I don't understand how I would implement it so that it can be used to update the 4 labels in my example code separately, though.
I want to show the progress (and update 4 dialog labels) as the program successfully finishes one job. The BackgroundWorker has only 1 DoWork() though. I have tried to use the e.Argument of the DoWorkEventArgs to differentiate between the different update methods but that attempt had failed.
public partial class BackgroundWorkerImportStatusDialog : Form
{
private BackgroundWorker dialogWorker = new BackgroundWorker();
private string path;
private string clientName;
public BackgroundWorkerImportStatusDialog()
{
InitializeComponent();
}
public void updateFileStatus(string path)
{
this.path = path;
dialogWorker = new BackgroundWorker();
dialogWorker.DoWork += new DoWorkEventHandler(updateLabels);
dialogWorker.RunWorkerAsync(UpdateComponent.FileStatus);
}
public void updatePrintStatus()
{
dialogWorker = new BackgroundWorker();
dialogWorker.DoWork += new DoWorkEventHandler(updateLabels);
dialogWorker.RunWorkerAsync(UpdateComponent.PrintStatus);
}
public void updateImportStatus(string clientName)
{
this.clientName = clientName;
dialogWorker = new BackgroundWorker();
dialogWorker.DoWork += new DoWorkEventHandler(updateLabels);
dialogWorker.RunWorkerAsync(UpdateComponent.ImportStatus);
}
public void updateArchiveStatus()
{
dialogWorker = new BackgroundWorker();
dialogWorker.DoWork += new DoWorkEventHandler(updateLabels);
dialogWorker.RunWorkerAsync(UpdateComponent.ArchiveStatus);
}
private void updateLabels(object sender, DoWorkEventArgs e)
{
MessageBox.Show(e.Argument.ToString());
if ((UpdateComponent) e.Argument == UpdateComponent.FileStatus)
{
t_filename.Text = path;
}
if ((UpdateComponent) e.Argument == UpdateComponent.PrintStatus)
{
t_printed.Text = "sent to printer";
}
if ((UpdateComponent) e.Argument == UpdateComponent.ImportStatus)
{
t_client.Text = clientName;
}
if ((UpdateComponent) e.Argument == UpdateComponent.ArchiveStatus)
{
t_archived.Text = "archived";
}
}
public enum UpdateComponent { FileStatus, PrintStatus, ImportStatus, ArchiveStatus}
And I can't imagine having 4 BackgroundWorkers for this pretty trivial dialog is the solution.
As I understand your question, you want to have your dialog form inform the user about 4 different aspects of your application running:
printing status
file status
import status
archiver status
Background worker could be used to periodically check each one. You may advanced progressbar by 25% after status of each operation is checked (and update your UI with appropriate information).
You may also try async programming - i.e. just start the operation, and lets your application continue. When the operation completes, your application will be notified, and could update information on the form.
Depending on the .NET framework you're using you may use async and await (avaialble since .NET 4.5 / C# 5 - async & await on MSDN) or classic approach to asynchronous programming.
Edit:
I am not sure that BackgroundWorker is the best solution in this situation. I can imagine having something like:
BackhgroundWorker checking things just once - i.e. check printing status once, file status once, import status once, archiver status once. This may sound silly, but it could be user behavior driver - i.e. explicitly launched when user clicks or invokes this mechanism any other way. ProgressBar could be put on the application's statausbar, so that user knows that 'application is actually doing something'.
Previous approach could be improved a bit - you never actually finish your job in BackgroundWorker - instead inside your main method you just have an infinite loop. This will allow you to check things periodically. In this approach there is no point in increasing the progress.
Sample for the second approach:
private void bg_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 1; i <= 10; i++)
{
if (worker.CancellationPending == true)
{
e.Cancel = true;
break;
}
else
{
CheckPrintingStatus();
CheckFileStatus();
CheckImportStatus();
CheckArchiverStatus();
System.Threading.Thread.Sleep(5000); // sleep for 5 seconds
}
}
}
There is a question if this solution (second approach) is better than having a thread created explicitly. You could think of creating 4 different threads, so that each could check something else. This would be a bit heavier on the OS, but on the other hand you can set different sleep times for every operation.
If you go for bare threads - you may want to use ThreadPool instead of creating threads explicitly.

Using Threads and .Invoke() and controls still remain inactive - C#

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.

Categories

Resources