Using Threads and .Invoke() and controls still remain inactive - C# - 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.

Related

How to make thread safe calls from FileSystemWatcher

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

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.

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

C# thread-safe calls on dynamically created controls

I know how to perform thread-safe calls on user controls, but when they are dynamically generated I don't have a clue. I tried to make an array list of them (rich text boxes) and I had problems with the 'parameter start thread' delegate after that. You input is much appreciated.
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.currentBox.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke( d, new object[] { text } );
}
else
{
this.currentBox.Text += text;
}
}
// This method is executed on the worker thread and makes
// a thread-safe call on the TextBox control. On a specific text box.....
private void ThreadProcSafe()
{
int i = 0;
while(_stop_threads == false) {
this.SetText(String.Format("{0}\n", i));
++i;
Thread.Sleep(100);
}
this.SetText(String.Format("Thread Time: {0}:{1}:{2}\n", DateTime.Now.TimeOfDay.Hours, DateTime.Now.TimeOfDay.Minutes, DateTime.Now.TimeOfDay.Seconds));
}
The problem is, although you have a separate thread for each UI control, that windows form works under a single thread. So, it is not safe updating UI controls from separate threads.
Maybe you may fill up a dictionary or array from those threads and update the results from that array under the Form's single thread. This way, you will be updating all the UI controls same time from the updated array of values.It is thread safe and it consumes less system resources.

WPF MVVM Multithreading Issue

I've seen other issues similar to mine, but i haven't seen any that i can apply to make my code work.
So i'm new to MVVM, and i'm trying to get some stuff that's executing in a background thread to update on my UI. What i'm noticing is that the first time bring up the UI, and the background thread executes the first time, if the collection is IEnumerable<> the UI isn't fully updated against backing data. If the collection is ObservableCollection<>, it throws an error.
From what i've read, changes to collections need to be executed on the dispatcher thread, but OnPropertyChanged() calls do not. So someone, please tell me how this could be happening:
I'm altering my _Printers observable collection:
foreach (PrinterViewModel pv in _Printers)
{
DispatcherExec(() =>
{
var abilities = from x in _ServerData.Types
select new PrinterAbility(
new PrintableType() { ID = x.ID, Name = x.Name, NumInProcUnit = x.NumInProcUnit, PrintersMappedTo = x.PrintersMappedTo, SysName = x.SysName },
x.PrintersMappedTo.Contains(pv.Printer.ID)
);
pv.Printer.SetAbilities(abilities);
});
My DispatcherExec looks like so:
private void DispatcherExec(Action action)
{
//Dispatcher.Invoke((Action)delegate
//{
// action.BeginInvoke(null, null);
//}, null);
Dispatcher.CurrentDispatcher.Invoke((Action)delegate
{
action.Invoke();
}, null);
}
And here's the SetAbilities code that fails:
public void SetAbilities(IEnumerable<PrinterAbility> abilities)
{
if (log.IsInfoEnabled)
log.Info("SetAbilities(IEnumerable<PrinterAbility> abilities): called on printer "+Name);
List<PrinterAbility> l = new List<PrinterAbility>();
abilities.ForEach(i =>
{
i.PrinterAbilityChanged += new PrinterAbilityChangedEventHandler(OnPrinterAbilityChanged);
l.Add(i);
}
);
lock (_Abilities)
{
foreach (PrinterAbility pa in l)
_Abilities.Add(pa);
}
if (log.IsDebugEnabled)
log.Debug("SetAbilities(IEnumerable<PrinterAbility> abilities): leaving");
}
On the _Abilities.Add(pa) observable collection add it says "This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread." I'm thinking, "are you joking?"
Further, I would think that a change to an object in the observable collection would automatically make it call OnCollectionChanged(), is that right?
Thanks in advance everyone.
Using Dispatcher.CurrentDispatcher is not something you should do from a BG thread. You need to use the Dispatcher for a DependencyObject-derived object that has been created on the UI thread.
Also, you're iterating over *ViewModel objects (PrinterViewModel) from within a BG thread. This really goes against MVVM. Your model should be doing asynchronous stuff, and your ViewModel(s) should be handling those asynchronous operations in a way that the view can consume (by marshalling to the proper thread via the Dispatcher).
Also, you're closing over a loop variable (pv). Bad, bad. This (depending on the order of execution) could mean that by the time the dispatcher comes around, you'll get multiple pv.Printer.SetAbilities(...) calls on the same PrinterViewModel instance. Create a local variable inside the loop and use that within your anonymous method to avoid this problem.
You should use the Dispatcher associated with any of your WPF controls, not the Dispatcher.CurrentDispatcher for the background thread.
Also
Dispatcher.CurrentDispatcher.Invoke((Action)delegate
{
action.Invoke();
}, null);
is redundant, it should be
wpfDispatcher.Invoke(action, null);
And finally for the first block, you should usually avoid passing loop variables to the lambdas, use temporary assignment trick to get around these sneaky closure problems. Almost certain that it's not the problem in this case though.

Categories

Resources