Code below runs OK. I wonder if it is really correct?
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
Parallel.ForEach(openFileDialog.FileNames, currentFile =>
{
try
{
StreamReader FileReader = new StreamReader(currentFile);
do
{
URLtextBox.Invoke(new MethodInvoker(delegate
{
URLtextBox.Text += SelectURLfromString(FileReader.ReadLine());
}));
}
while (FileReader.Peek() != -1);
FileReader.Close();
}
catch (System.Security.SecurityException ex)
{
...
}
catch (Exception ex)
{
...
}
});
}
Otherwise I get either "Cross-thread operation not valid. Control 'URLtextBox' accessed from another thread" or stuck application.
The code is correct - you need to use Invoke to refresh controls from outside the GUI thread. However, you are excuting the SelectURLfromString(FileReader.ReadLine()); method in the GUI thread as well, you should replace that by
string url = SelectURLfromString(FileReader.ReadLine());
URLtextBox.Invoke(new MethodInvoker(delegate
{
URLtextBox.Text += url;
}));
to minimize the work in the GUI thread to a minimum.
You cannot update UI controls from worker threads safely, unless you marshall onto the UI thread.
Take a look at TaskScheduler.FromCurrentSynchronizationContext
How to: Schedule Work on a Specified Synchronization Context
The code is correct, you need the Invoke call so that the control is updated in the GUI thread.
However, there are some other things that doesn't really make sense in the code:
You are doing parallel operations that is using a resource that is not parallel. Your threads will be fighting for attention from the disk, which is clearly the bottle neck due to it's relatively low speed.
You will read lines from several files, and dump them intermixed in a textbox. That might be all right in this specific situation, but generally it gives an unpredictable result.
You are using the += operation to concatenate strings, a method that is notorious for it's bad scalability. It might not be a big problem in this case though, as the disk bottle neck is probably a lot worse.
The Invoke is necessary because controls are bound to the thread that created their associated User32 window (often called an HWND). That said, you could probably optimize a little by reading and processing the contents of the file outside of the Invoke's delegate.
Related
EDIT:
please see question history, for unchanged question in order not to invalidate comments.
I am clicking button that executes certain codes and it creates a thread (System.Threading.Thread). When I reclick button which starts process it hangs and freezes ui. What could be the reason?
public partial class ucLoader : UserControl
{
//lock object for whole instance of class ucLoader
private object lockUcLoader = new object();
//bringing info from ui
private void btnBringInfo_Click(object sender, EventArgs e)
{
lock (lockUcLoader)
{
btnBringInfo_PerformClick(false);
}
}
//using this method because it could be called when even button not visible
internal void btnBringInfo_PerformClick(bool calledFromBandInit)
{
lock (lockUcLoader) //HANGS HERE when called multiple times and ui freeze as well
//by the way I am using (repetitive) lock, because this method also called independently from btnBringInfo_Click
{
//...
this.btnLoad_PerformClick();
}
}
//Another button perform click that could be triggered elsewhere when even button not visible
private void btnLoad_PerformClick()
{
lock (lockUcLoader) //I am using (repetitive) lock, because this method also called independently from btnBringInfo_PerformClick
{
//...
Run();
}
}
//method for creating thread which System.Threading.Thread
private void Run()
{
lock (lockUcLoader) //Maybe this lock is NOT REQUIRED, as it is called by only btnLoad_PerformClick(), could you please confirm?
{
//some code that thread can be killed when available, you can ingore this two lines as they are irrelevant to subject, I think
Source = new CancellationTokenSource();
Token = Source.Token;
var shell = new WindowsShell();
Thread = new Thread((object o) =>
{
//...
var tokenInThread = (CancellationToken)o;
exitCode =TaskExtractBatchFiles(cls, shell, exitCode);
using (var logEnt = new logEntities())
{
//Do some db operation
//...
this.Invoke((MethodInvoker)delegate
{
//do some ui update operation
//...
});
}
}
Thread.Start(Token);
}
}
public void Progress(string message)
{
Invoke((MethodInvoker)delegate //ATTENTION HERE see below picture Wait occurs here
{
if (message != null && message.Trim() != string.Empty)
{
this.txtStatus.AppendText(message + Environment.NewLine);
}
});
}
}
In order to avoid get closed question, what my question is how can I prevent
below method can be accesses with out lock from background thread and ui thread
public void Progress(string message)
{
Invoke((MethodInvoker)delegate //ATTENTION HERE see below picture Wait occurs here
{
if (message != null && message.Trim() != string.Empty)
{
this.txtStatus.AppendText(message + Environment.NewLine);
}
});
}
Invoke((MethodInvoker)delegate ...
Whenever you use the lock statement in your code then you always run the risk of inducing deadlock. One of the classic threading bugs. You generally need at least two locks to get there, acquiring them in the wrong order. And yes, there are two in your program. One you declared yourself. And one you cannot see because it is buried inside the plumbing that makes Control.Invoke() work. Not being able to see a lock is what makes deadlock a difficult problem to debug.
You can reason it out, the lock inside Control.Invoke is necessary to ensure that the worker thread is blocked until the UI thread executed the delegate target. Probably also helps to reason out why the program deadlocked. You started the worker thread, it acquired the lockUcLoader lock and starts doing its job, calling Control.Invoke while doing so. Now you click the button before the worker is done, it necessarily blocks. But that makes the UI thread go catatonic and no longer capable of executing the Control.Invoke code. So the worker thread hangs on the Invoke call and it won't release the lock. And the UI thread hangs forever on the lock since the worker can't complete, deadlock city.
Control.Invoke dates from .NET 1.0, a version of the framework that has several serious design mistakes in code related to threading. While meant to be helpful, they just set death-traps for programmers to blunder into. What is unique about Control.Invoke is that it is never correct to use it.
Distinguish Control.Invoke and Control.BeginInvoke. You only ever need Invoke when you need its return value. Note how you don't, using BeginInvoke instead is good enough and instantly solves the deadlock. You'd consider Invoke to obtain a value from the UI so you can use it in the worker thread. But that induces other major threading issue, a threading race bug, the worker has no idea what state the UI is in. Say, the user might be busy interacting with it, typing a new value. You can't know what value you obtain, it will easily be the stale old value. Inevitably producing a mismatch between the UI and the work being done. The only way to avoid that mishap is to prevent the user from typing a new value, easily done with Enable = false. But now it no longer makes sense to use Invoke, you might as well pass the value when you start the thread.
So using BeginInvoke is already good enough to solve the problem. But that is not where you should stop. There is no point to those locks in the Click event handlers, all they do is make the UI unresponsive, greatly confuzzling the user. What you must do instead is set the Enable properties of those buttons to false. Set them back to true when the worker is done. Now it can't go wrong anymore, you don't need the locks and the user gets good feedback.
There is another serious problem you haven't run into yet but you must address. A UserControl has no control over its lifetime, it gets disposed when the user closes the form on which it is hosted. But that is completely out of sync with the worker thread execution, it keeps calling BeginInvoke even though the control is dead as a doornail. That will make your program bomb, hopefully on an ObjectDisposedException. A threading race bug that a lock cannot solve. The form has to help, it must actively prevent the user from closing it. Some notes about this bug in this Q+A.
For completeness I should mention the third most common threading bug that code like this is likely to suffer from. It doesn't have an official name, I call it a "firehose bug". It occurs when the worker thread calls BeginInvoke too often, giving the UI thread too much work to do. Happens easily, calling it more than about thousand times per second tends to be enough. The UI thread starts burning 100% core, trying to keep up with the invoke requests and never being able to catch up. Easy to see, it stops painting itself and responding to input, duties that are performed with a lower priority. That needs to be fixed the logical way, updating UI more than 25 times per second just produces a blur that the human eye can't observe and is therefore pointless.
I'm need to display some form of feedback to the user, while a small process (7-10 seconds) takes place in the background.
I had no issues in the past using separate threads and BackgroundWorkers in Windows Forms, but its proving difficult in WPF.
I have read many articles, in this respect, and how I should be using dispatchers in WPF to start a new thread, etc. However, when I try to use a BackgroundWorker to display a form of waiting image feedback, it simply remains static.
I don't believe that it matters, but it uses mui from FirstFloor (https://github.com/firstfloorsoftware/mui).
I'm trying to use the built-in ProgressRing feature (which works no problems when run within the same thread and there are no other major tasks running in the background.
Adding a BackgroundWorker, brings an exception due to cross thread access of objects, even though many blogs states that BackgroundWorks in WPF are cross thread aware and safe to run.
The following is the closest code that generates what I need.
private async void MyTaskProcess()
{
await Dispatcher.BeginInvoke(DispatcherPriority.Send, new ThreadStart(() =>
{
try
{
//Update the waiting ring image
ProgressRing.IsActive = true;
}
catch
{
ProgressRing.IsActive = false;
MessageBox.Show("Exception Thrown");
}
}));
await Dispatcher.BeginInvoke(DispatcherPriority.Background, new ThreadStart(() =>
{
try
{
//Run the main MS Excel export function
Export2Excel();
ProgressRing.IsActive = false;
}
catch
{
MessageBox.Show("Exception Thrown");
}
}));
}
Any feedback is appreciated.
The way you do this in a modern WPF application is to start a new Task in which you do the work; under the covers this will perform the work on a thread pool thread:
Task.Factory.StartNew(this.DoWork)
Now in DoWorkto report progress you InvokeAsync back to the main thread whenever the porgress count changes:
void DoWork()
{
foreach(var item in this.WorkItems)
{
// Do something
// Report Progress
++progress
Application.Current.Dispatcher.InvokeAsync(() => this.Progress = progress);
}
}
Adding a BackgroundWorker, brings an exception due to cross thread access of objects, even though many blogs states that BackgroundWorks in WPF are cross thread aware and safe to run.
BackgroundWorker works fine with WPF, as long as you create and start the BGW on the UI thread. (As a side note, BGW has the same restriction on Windows Forms). As other commenters have noted, the proper way to do progress updates with BGW is using ReportProgress, not Dispatcher.
However, I'd recommend using the newer Task.Run with IProgress<T> for progress updates. I have a blog post that compares/contrasts the old BGW progress updates with the new IProgress<T>-based progress updates.
It's difficult to say what your code should look like, since the code you posted doesn't actually run anything on a background thread. In particular, if Export2Excel must be run on the UI thread, and that's all your work is doing, then there's no point in using BGW or Task.Run at all, since nothing can run on the background thread anyway.
you go, to the below link written by me and read carefully.I hope you will definetily solve your problem:
Threads in WPF
I've got the following code,
As you can see the background worker searches for files and in the progress changed event files are added to a listview, however since there is lots of files being added to the listview, the UI becomes unresponsive, I could Sleep the thread in the loops but I don't think that's a good practice, what's the best way to prevent the UI freeze?
to elaborate more, listview is a form control on a form.
void bg_DoWork(object sender, DoWorkEventArgs e)
{
Stack<string> dirs = new Stack<string>(20);
dirs.Push(e.Argument.ToString());
while (dirs.Count > 0)
{
string currentDir = dirs.Pop();
string[] subDirs;
try { subDirs = System.IO.Directory.GetDirectories(currentDir); }
catch (UnauthorizedAccessException) { continue; }
catch (System.IO.DirectoryNotFoundException) { continue; }
string[] files = null;
try { files = System.IO.Directory.GetFiles(currentDir); }
catch (UnauthorizedAccessException) { continue; }
catch (System.IO.DirectoryNotFoundException) { continue; }
foreach (var file in files) { bg.ReportProgress(0, file); }
foreach (string str in subDirs) { dirs.Push(str); }
}
}
void bg_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
listView1.Items.Add(e.UserState.ToString());
}
So the issue here is that ReportProgress is actually asynchronous. It doesn't wait for the corresponding UI updates to actually be made before it continue moving on doing work. Normally this is great. In most situations there's no compelling reason to slow down your productive work just to go wait for UI updates.
There's one exception though. If you call ReportProgress so often that it doesn't actually have time to complete the previous progress update before the next one is added, what ends up happening is that you fill up the message queue with requests to go update progress. You have so many files, and getting those lists of files takes very little time. It actually takes quite a bit less time than it takes to marshal to the UI thread and update the UI.
Because this queue ends up being backed up, any other UI updates need to sit through that long queue before they can do anything.
Batching up the updates and indicating progress less often is one possible solution. It may or may not be acceptable, given your situation. It will almost certainly help, but depending on just how long it takes the UI to be updated based on whatever it is that you're doing, and how quickly you can generate data, it's possible that even this will cause problems. If it works for your specific case though, great.
The other option is to change how you update progress such that your worker waits for the UI update before continuing. Obviously this is something that you should avoid unless you need to do it, because it means that while you aren't freezing the UI while you do your work, your work will take a fair bit longer. While there are any number of ways of doing this, the simplest of which is likely to just use Invoke (not BeginInvoke):
foreach (var file in files)
listView1.Invoke(new Action(()=>listView1.Items.Add(file));
While calling Invoke from a BackgroundWorker is generally code smell, and should be avoided, this is a somewhat exceptional case.
Note that even if you do end up resorting to using Invoke here I would still suggest batching the invokes such that you add more than just one item per invoke. If the number of files in a single directory is sufficiently low, put the whole foreach inside the Invoke, and if your subdirectories tend to have very few files (i.e, they're very deep, not broad) consider even putting all of the files into a temp list until it's large enough to be worth batching into an Invoke. Play around with different approaches based on your data, to see what works best.
bg.ReportProgress() is meant to report the overall progress of the BackgroundWorker back to the UI thread, so you can inform your users as to the progress. However, you're using it to actually add strings to a ListView. You're better off compiling the list of files into an in-memory list and then populating listView1 once when the background worker completes:
public void bg_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
foreach (var file in MyFileListVar){
listView1.Items.Add(file);
}
}
Try to load multiple files (lets say between 10~50), then send them back to the UI thread (ie: bg.ReportProgress) instead of sending every file separately.
using c#, .Net Framework 4.5, VS 2012
Try to use Parallel.Foreach
As result has some UI and add method for button (method allow to rotate all pic in folder and save in another place)
private void ProcessFileParallel()
{
string[] files =
Directory.GetFiles(#"C:\Users\Public\Pictures\Sample Pictures",
"*.jpg", SearchOption.AllDirectories); //get source folder
string dirNew = #"C:\modifiedImages"; //new folder
Directory.CreateDirectory(dirNew); //create dir
//usage of parallel and lambda
Parallel.ForEach(files, currfiles =>
{
string fileName = Path.GetFileName(currfiles); //get cur name of file
//GC for Bitmap
//create new object of Bitmap
using (Bitmap bitmap = new Bitmap(currfiles))
{
bitmap.RotateFlip(RotateFlipType.Rotate180FlipX); //rotating
bitmap.Save(Path.Combine(dirNew, fileName)); //save as
//anonym delegate - used for safety access to UI elements from secondary thread
this.Invoke((Action)delegate
{
//caption name change for form
this.Text =
string.Format("Curr Thread {0}",
Thread.CurrentThread.ManagedThreadId);
}
);
}
}
);
}
it's work, but after end (when all pic rotated and saved in new place, and UI got at top something like Curr Thread 11) primary thread is locked - means UI not active - can't do anything.
Question - How can i unlock my UI elements?
Parallel.ForEach blocks the thread until all loops are complete. If you want your UI to stay responsive you need to run it for instance in a Task. Also see this question, which is basically the same:
Does Parallel.ForEach Block?
I started out writing a comment, but it quickly became too long.
First off, I agree that ChrisK's answer is, indeed, a good solution to the problem. As an academic exercise, however, I think it's worth explaining why the issue happens in the first place.
Parallel.ForEach blocks the thread it's called on (which is the UI thread in your case) until all parallel operations have completed. At the same time you've got other threads trying to synchronously invoke actions on the UI thread (which is already blocked). Those invokes have to wait for the UI thread to unblock, but it won't unblock until all non-UI threads have finished their work (which they can't, because they're waiting). Checkmate. You have a deadlock, and your call to Parallel.ForEach will never complete.
Realistically you could have solved your issue by simply substituting this.Invoke with this.BeginInvoke, which posts work to the UI thread asynchronously and thus allows non-UI threads to keep going and eventually complete; however I do maintain that offloading the actual call to Parallel.ForEach to the thread pool via a Task (as suggested by ChrisK) is a better solution all around.
P.S. On an unrelated note, your call to Thread.CurrentThread.ManagedThreadId is always invoked on the UI thread, and therefore will always return the ID of your UI thread, which may or may not be the thread doing the work on the image. If you want to know which actual thread is doing the work you would have to store the return value from Thread.CurrentThread.ManagedThreadId outside of your delegate definition, and then close over it.
I recently updated an application from VS2003 to VS2008 and I knew I would be dealing with a host of "Cross-thread operation not valid: Control 'myControl' accessed from a thread other than the thread it was created on" I am handling this in what I beleive is the correct way (see code sample below). I am running into numerous controls that are going to need a similar fix. Not wanting to have similar code for every label, textbox etc.. that are being accessed by a non UI thread. What are the ramifications of just setting the CheckForIllegalCrossThreadCalls = false for the entire app?
I found a CodeProject article with various workarounds and a warning at the bottom to NOT set the property. I am looking for other opinions/experiences on this issue.
private void ShowStatus(string szStatus)
{
try
{
if (this.statusBar1.InvokeRequired) { BeginInvoke(new MethodInvoker(delegate() { ShowStatus(szStatus); })); }
else { statusBar1.Panels[0].Text = szStatus; }
}
catch (Exception ex)
{
LogStatus.WriteErrorLog(ex, "Error", "frmMNI.ShowStatus()");
}
}
I found another article with some possible solutions SO Question 2367718
When you're not debugging, you'll still have problems.
From the documentation of Control.CheckForIllegalCrossThreadCalls:
Note that illegal cross-thread calls will always raise an exception when an application is started outside the debugger.
You'll need to correct the problems.
That being said, you mentioned:
Not wanting to have similar code for every label, textbox etc.. that are being accessed by a non UI thread.
I would reconsider this stance. You should try to move the logic running on a separate thread into separate methods or classes, which will in turn make marshaling the calls back into the UI much simpler. Over time, this will make your code much more reliable and maintainable.
Note that you can use Control.Invoke to marshal a whole set of calls to the UI in one call, too, instead of doing each single set operation individually. There really shouldn't be that many of them, when you finish.
Edit:
For example, it sounds like you're loading the data. Say you have (on your background thread), your data loading method:
var myData = LoadData();
this.Invoke( new Action( () =>
{
// Just set all of your data in one shot here...
this.textBox1.Text = myData.FirstName;
this.textBox2.Text = myData.LastName;
this.textBox3.Text = myData.NumberOfSales.ToString();
}));
The remarks section of the documentation for CheckForIllegalCrossThreadCalls makes it pretty clear that doing so is not a good idea
When a thread other than the creating
thread of a control tries to access
one of that control's methods or
properties, it often leads to
unpredictable results. A common
invalid thread activity is a call on
the wrong thread that accesses the
control's Handle property. Set
CheckForIllegalCrossThreadCalls to
true to find and diagnose this thread
activity more easily while debugging.
Note that illegal cross-thread calls
will always raise an exception when an
application is started outside the
debugger.