backgroundworker and different thread - c#

I have a WinForm application and want to convert to WPF, after the 200 errors was clean, I'm trying to get works and always have one errors I'm not able to resolve.
At my BackgroundWorker1_DoWork I'm trying to retrieve how many files in a directory by using this:
//Set the progress bar
Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Render,
new Action(() => {
Directory.GetFiles((textBox_UpdatedBuildPath.Text, "*",
SearchOption.AllDirectories).Length;
})
); //<-----------------HERE
and get following error:
The calling thread cannot access this object because a different
thread owns it.
After quick search, it's seemed to be related when user want to update UI, but it seems that it is not my case, I need informations from Textbox and recursively find how many files folder can contains.
Thank's for future answer.
Edit: Here the code with full comment -> http://codepaste.net/r1qejd

Almost every WPF element has thread affinity. This means that access to such an element should be made only from the thread that created the element. In order to do so, every element that requires thread affinity is derived, eventually, from DispatcherObject class. This class provides a property named Dispatcher that returns the Dispatcher object associated with the WPF element.
The Dispatcher class is used to perform work on his attached thread. It has a queue of work items and it is in charge of executing the work items on the dispatcher thread.
You can find on the following link some more details on the subject.
As MSDN says:
Only one thread can modify the UI thread. But how do background
threads interact with the user? A background thread can ask the UI
thread to perform an operation on its behalf. It does this by
registering a work item with the Dispatcher of the UI thread. The
Dispatcher class provides two methods for registering work items:
Invoke and BeginInvoke. Both methods schedule a delegate for
execution. Invoke is a synchronous call – that is, it doesn’t return
until the UI thread actually finishes executing the delegate.
BeginInvoke is asynchronous and returns immediately.
So try to use Dispatcher. For example:
int CountFileToCheck;
textBox_UpdatedBuildPath.Dispatcher.Invoke(() => {
CountFileToCheck=Directory.GetFiles(textBox_UpdatedBuildPath.Text, "*", SearchOption.AllDirectories).Length;
});
Update:
I've made an axample for you and tested it. It works perfectly.
XAML:
<StackPanel>
<Button Name="btn" Click="btn_Click" Content="Foo Button"/>
<TextBox Name="textBox" Text="123"/>
</StackPanel>
Code-behind:
public partial class MainWindow : Window
{
BackgroundWorker bw;
public MainWindow()
{
InitializeComponent();
bw = new BackgroundWorker();
}
private void btn_Click(object sender, RoutedEventArgs e)
{
bw.DoWork += (o, a) =>
{
try
{
string str = "";
textBox.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
{
str = textBox.Text;
MessageBox.Show(str);
}));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
//e.Result = LoadXmlDoc();
};
bw.RunWorkerAsync();
}
}

Related

Create complex UserControl during runtime without freezing the GUI

I have an very complex UserControl which needs to be created at runtime. This creation freezes the GUI for about 5 seconds (which is not acceptable).
I tried to move this operation into an Background Worker and end up with this Exception:
The calling thread must be STA, because many UI components require this.
I'm aware that i can't use an MTA thread to create an UserControl / UI Element. I tried to use a combination of BackgroundWorker and Dispatcher but it didn't work.
First try
private void LetsGo()
{
var backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += backgroundWorker_DoWork;
backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
backgroundWorker.RunWorkerAsync();
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
DispatcherOperation dispatcherOperation = Dispatcher.CurrentDispatcher.BeginInvoke(new Action(this.GenerateControlAsync), DispatcherPriority.Background);
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
// Cancelled
}
else if (e.Error != null)
{
//Exception Thrown
}
else
{
//Completed
}
}
private void GenerateControlAsync () {
this.Control = new TimeConsumingUserControl();
}
The code above is not working, the method this.GenerateControlAsync isn't executed.
Second try
private async void GenerateControl()
{
this.Control = await Dispatcher.CurrentDispatcher.InvokeAsync<UserControl>(this.GenerateControlAsync);
}
private UserControl GenerateControlAsync()
{
return new TimeConsumingUserControl();
}
this sample is working but it keeps freezing the GUI thread.
I'm using WPF and .net Framework 4.5.
Note that the method GenerateControlAsync() does not simply create an instance of an UserControl, there is a lot more logic involved.
To answer #HighCore's question:
In fact the XAML Code of the UserControl is transformed out of xml files via XSLT and the Codebehind is generated using CodeDOM. The whole things needs to be compiled and wrapped into an assembly. I use assembly.CreateInstance() to get an instance of the UserControl. This line throws the quoted exception. In my GUI i have a ContentControl which has a binding to the UserControl in my ViewModel. The data for the generation like the xml files which need to be transformed are retrieved from a webservice.
This is why the execution of this method takes a bit longer than someone might expect.
From your description of all the steps involved in creating your control it looks like you're lumping together a lot of work that doesn't need to be done on the same thread and trying to do it all on either the UI or background thread. You should instead be doing the minimum amount of work necessary on the UI thread (the actual UserControl instantiation) and doing everything else on a worker thread. Rather than a single async unit of work you should be doing 2 steps, which with async-await is very simple. It should look more like this:
var dynamicAssembly = await this.GenerateControlAssemblyAsync();
this.Control = this.GenerateControlFromAssembly(dynamicAssembly);
Because of the way await works the second line will automatically be run back on the original (UI) thread so no need for any Dispatcher calls. In GenerateControlAssemblyAsync you should use a Task.Run and do all of the other code in that. GenerateControlFromAssembly should be doing nothing but instantiating the UC instance.
You need to chunk your Assembly generation. Your assembly generation takes too much time and you need to put everything in another thread, and only the DIRECT Ui component generation in same thread.
There is cool method that can load Xaml from stream and do it in chunks, without chogging up UI. Have a look: http://msdn.microsoft.com/en-us/library/aa346591.aspx

File Dialog from a Background Worker

While maintaining some code, I discovered that we have an infinite hang-up in a background worker. The worker requires access to a script file. The original code was written to pop up a file dialog if no script file was defined, to allow the user to select one. It looks something like this:
private void bgworker_DoWork(object sender, DoWorkEventArgs e)
{
... snip ...
if (String.IsNullOrWhitespace(scriptFile))
{
scriptFile = PromptForScript();
}
... snip ...
}
private string PrompForScript()
{
string script = "";
OpenFileDialog openDialog = new OpenFileDialog();
if (openDialog.ShowDialog() == DialogResult.OK)
{
script = openDialog.FileName;
}
return script;
}
I've read up a bit about MethodInvoker, but almost all of the invoke methods require that you call them from a control. The background worker in question is running from a separate class, which doesn't extend Control. Do I use the form that calls the class with the bgworker for that? Or is there another way of interrupting the thread for user input?
It's not recommended to invoke the UI from the background worker DoWork event handler. BackgroundWorker is meant to do work on a non-UI thread to keep the UI responsive. You should ask for any file information before starting the BackgroundWorker object with RunWorkerAsync.
What you want to do is capture the SynchronizationContext on the UI thread and pass that along to the background worker. The BackgroundWorker can call Send() (synchronous, like Invoke) and Post() (asynchronous, like BeginInvoke) on the context to invoke back to the correct UI thread. That said, there is probably no need for the BackgroundWorker in this case- a regular threadpool thread would do just fine.
This (slightly modified) block of code from http://msmvps.com/blogs/manoj/archive/2005/11/03/74120.aspx should give you the general idea:
private void button1_Click(object sender, EventArgs e)
{
// Here we are on the UI thread, so SynchronizationContext.Current
// is going to be a WindowsFormsSynchronizationContext that Invokes properly
ctx = SynchronizationContext.Current;
ThreadPool.QueueUserWorkItem(
// This delegate is going to be invoked on a background thread
s => {
// This uses the context captured above to invoke
// back to the UI without the "messy" referencing
// of a particular form
ctx.Send(s2 =>
{
// Interact with your UI here- you are on the UI thread
},null);
}
);
}
If some Form kicks off a long-running process within another class that uses a BGworker, why wouldn't the form (or presenter, depending on UI architecture) handle the processing of the error state?
Perhaps, just pass back some status result (or throw a very targeted, specific exception that you can handle in the UI)?
Leave the background worker to determine if there IS an error, but leave handing the error (especially the UI portion of showing a message box) to the upper layers.
Sorry this didn't have more concrete code but it could go a lot of different ways depending on how your system is architected.
Well, the Form class has an Invoke method, so passing the form instance to the background working class should work.

Dispatcher.Dispatch on the UI thread

I have the doubt regarding when to use the Dispatcher.Invoke to update something on UI from different Thread.
Here's my code...
public Window4()
{
InitializeComponent();
this.DataContext = this;
Task.Factory.StartNew(() => Test() );
}
private List<string> listOfString = new List<string>();
public List<string> ListOfString
{
get { return listOfString; }
set { listOfString = value; }
}
public void Test()
{
listOfString.Add("abc");
listOfString.Add("abc");
listOfString.Add("abc");
}
<Grid>
<ListView ItemsSource="{Binding ListOfString}" />
</Grid>
I am starting a new Task on the different Thread, do i need to use Dispatcher.BeginInvoke to update the UI.
In this case it is updating the UI, but i've seen some scenarios where people update UI using Dispatcher.Invoke or BeginInvoke from the different Thread.
So my question is when we have to do that and why in this case it is working fine.
Thanks & Regards,
BHavik
I have the doubt regarding when to use the Dispatcher.Invoke to update
something on UI from different Thread.
When you are on a different thread you will always have to use the dispatcher to update a ui component that belongs to another thread.
I am starting a new Task on the different Thread, do i need to use
Dispatcher.BeginInvoke to update the UI.
Tasks allow for multiple operations to be performed without blocking the thread they are called from but that doesn't mean they are on a different thread. However when updating the UI from inside a Task you will need to use the dispatcher.
In this case it is updating the UI, but i've seen some scenarios where
people update UI using Dispatcher.Invoke or BeginInvoke from the
different Thread.
Invoke will block the calling thread while it is performing the action and BeginInvoke will not. BeginInvoke will return control immediately to the caller, Invoke may cause the calling thread to hang if it is performing a heavy operation.
This is from msdn documentation,
In WPF, only the thread that created a DispatcherObject may access
that object. For example, a background thread that is spun off from
the main UI thread cannot update the contents of a Button that was
created on the UI thread. In order for the background thread to access
the Content property of the Button, the background thread must
delegate the work to the Dispatcher associated with the UI thread.
This is accomplished by using either Invoke or BeginInvoke. Invoke is
synchronous and BeginInvoke is asynchronous.
Edit: In response to your comment I ran some tests.
When calling Test() from a task (without using the dispatcher) I got this error "The calling thread cannot access this object because a different thread owns it."
So I created a method called PrintThreadID(). I printed the thread before entering the task then from inside the task and it does report both are running on the same thread ID.
The error is misleading because it says the calling thread is different than the one that owns it which the PrintThreadID() function shows is not true, they are in fact on the same thread. Tasks while on the same thread still cannot update a UI component without using Dispather.Invoke().
So here is a working example which will update the Grid from a task.
public partial class MainWindow : Window
{
public List<string> myList { get; private set; }
public MainWindow()
{
InitializeComponent();
myList = new List<string>();
label1.Content = Thread.CurrentThread.ManagedThreadId.ToString();
Task.Factory.StartNew(PrintThreadID);
Task.Factory.StartNew(Test);
}
private void PrintThreadID()
{
label1.Dispatcher.Invoke(new Action(() =>
label1.Content += "..." + Thread.CurrentThread.ManagedThreadId.ToString()));
}
private void Test()
{
myList.Add("abc");
myList.Add("abc");
myList.Add("abc");
// if you do not use the dispatcher you will get the error "The calling thread cannot access this object because a different thread owns it."
dataGrid1.Dispatcher.Invoke(new Action(() =>
{
dataGrid1.ItemsSource = myList.Select(i => new { Item = i });
}));
}
}
Your test isn't valid as it isn't actually updating your UI. If you want proof, add this sleep call:
public void Test()
{
Thread.Sleep(10000);
listOfString.Add("abc");
listOfString.Add("abc");
listOfString.Add("abc");
}
You'll find that your UI appears and the list is empty. 10 seconds, 30 seconds, 3 months later, the list won't contain your strings.
Instead your test is demonstrating a race condition - your Test() method is completing fast enough that the strings are added to the list before the UI appears on screen and reads the list.
To fix it, change your collection to an ObservableCollection<string>. But then you'll encounter the next problem - you can't update an ObservableCollection on a background thread. So that's where the Dispatcher comes in.

Instantiating an object in a background worker causes the UI to freeze up

This is what i have at the moment, but when i call Dispatcher.BeginInvoke(), my UI freezes up:
BackgroundWorker backgroundWorker = new BackgroundWorker();
backgroundWorker.WorkerSupportsCancellation = true;
backgroundWorker.WorkerReportsProgress = true;
ViewModel.GenerateReport(backgroundWorker, Dispatcher);
ViewModel:
backgroundWorker.DoWork += delegate(object s, DoWorkEventArgs args)
{
try
{
ReportViewModel reportViewModel = new ReportViewModel(SessionContext, Mediator, reportDashboardViewModel.ReportRequest, false);
dispatcher.BeginInvoke((Action)(() =>
{
ReportPreviewView reportPreviewView = new ReportPreviewView(reportViewModel);
reportPreviewView.ReportName = reportDashboardViewModel.ReportRequest.Report.ReportName;
ReportView = reportPreviewView;
}));
}
catch (Exception exception)
{
backgroundWorker.ReportProgress(0, "There Was an Error generating the report");
backgroundWorker.CancelAsync();
throw;
}
};
backgroundWorker.RunWorkerAsync();
The first line in dispatcher.BeginInvoke causes my UI to freeze up.
ReportPreviewView reportPreviewView = new ReportPreviewView(reportViewModel);
Note: ReportPreviewView creates the relevant view for the report request. May it be Devexpress, C1 or pivot reports.
As soon as i remove the dispatcher.BeginInvoke, i get this error:
The calling thread must be STA, because many UI components require this.
So my question is, what do i need to do to get around this?
The whole reason for using a BackgroundWorker was so that my UI stays responsive at all times.
I am new to multithreading,so maybe i got the structure all wrong...
Disptacher.BeginInvoke launches the code you encapsulated withing an Action ansynchronoulsy, but the Do_Work handler is also updated async by the BackgroundWorker, so you should't use it.
The main problem is that you are trying to access an UI instance from another thread, and windows disallow that: only the main thread can access an UI element instance.
The correct way to do it is using Control.Invoke if you are using windows Forms.
A deep article about the winforms threading model
Moral of the story, all UI elements needs to be created on the UI thread!
That being said, if the creation of the UI element takes a long time, the UI will freeze up. Take all the heavy lifting and put it in a Task or BackgroundWorker.
Then you will have a more responsive UI.

C# How does a background thread tell a UI thread that it has finished doing something?

Scenario
Lets say you have a C# WinForms application that doing some data processing.
You have a method that retrieves data from a database that is called by the UI thread.
The background thread then runs off to do this task.
You want the UI to carry on doing its thing and not be locked up and unresponsive.
QUESTION
How do you let the background thread run off and do its processing and then automatically alert the UI thread when it has returned the results?
If you don't use a background worker thread (for whatever reason) then you must fire an event from your thread which is handled by the UI thread. For example I have this code that scans my mp3s and fires and event for each album found and then another event when it finished (or is stopped):
public void Build()
{
FindAlbums(Root);
// Final update
if (Library_Finished != null)
{
Library_Finished(this, null);
}
}
private void FindAlbums(string root)
{
// Find all the albums
string[] folders = Directory.GetDirectories(root);
foreach (string folder in folders)
{
string[] files = Directory.GetFiles(folder, "*.mp3");
if (files.Length > 0)
{
// Add to library - use first file as being representative of the whole album
var info = new AlbumInfo(files[0]);
if (Library_AlbumAdded != null)
{
Library_AlbumAdded(this, new AlbumInfoEventArgs(info));
}
}
FindAlbums(folder);
}
}
Then in the UI thread (this is WinForms code):
private void Library_AlbumAdded(object sender, AlbumInfoEventArgs e)
{
if (dataGridView.InvokeRequired)
{
dataGridView.Invoke((MethodInvoker)delegate { AddToGrid(e.AlbumInfo); });
}
else
{
AddToGrid(e.AlbumInfo);
}
}
private void Library_Finished(object sender, EventArgs e)
{
if (dataGridView.InvokeRequired)
{
dataGridView.Invoke((MethodInvoker)delegate { FinalUpdate(); });
}
else
{
FinalUpdate();
}
}
I would, however, recommend that you investigate the background worker thread, as it does so much of the housekeeping for you. However, the same handling code would be needed in the RunWorkerCompleted event to update the UI.
There are several ways of doing this, but the easiest way is to use a BackgroundWorker.
Essentially it has two delegates, the DoWork and the WorkCompleted. DoWork executes on a seperate thread and the WorkCompleted callback happens on the UI thread.
Here's more info:
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
You can use the BackgroundWorker to do your time-intensive processing in its DoWork event handler. Then handle the RunWorkerComplete event -- it will fire when the DoWork method is finished. While all this is going on, your UI thread will be happily running along.
If you're using .NET 2.0 or newer, then this is made much easier with the BackgroundWorker thread. It has its own RunWorkerCompleted event that does just what you need.
I would highly recommend the BackgroundWorker in fact. It has the functionality most developers are after when creating threads. They're also easier to cancel gracefully, and they even have the ability to report progress.
Try to use BackgrounWorker and register a handler to the its RunWorkerCompleted event.
In Winforms you can use the .Invoke method (and check the .InvokeRequired property) to marshall a call back to the UI thread. You don't so much notify the UI thread - it keeps going on and doesn't wait for any sort of a completion, but you can interact with a control (for example, update the text property of a label) from another thread using the Invoke method.
You can also use the BackgroundWorker object (read MSDN to find out more about it), which implements a callback functionality to run some code on the UI thread after the background work is completed.
If you are talking about a WinForm app, you can make changes to any UI objects using the Invoke method on your form (or any of the controls on the form). You can also find useful the InvokeRequired property
You can store a reference to the UI thread Dispatcher by using Dispatcher.CurrentDispatcher (obviously in a method called by GUI thread). Using this object you can use the BeginInvoke or Invoke methods in your working thread to execute a method on the GUI thread notifying it that you have completed work. Personally I find this method to be slightly more flexible than using a background worker object and can produce slightly more readable code.
There's an easy way of working with multiple threads in C#. It is called BackgroundWorker.
You should check it out: BackgroundWorker Tutorial
As was mentioned many times, the BackgroundWorker class can be used.
Alternatively, you could do something akin to the following:
void buttonGo_Clicked( object sender, EventArgs e )
{
MyAsyncClass class = new MyAsyncClass();
class.LongOperationFinished += (LongOperationFinishedEventHandler)finished;
class.BeginLongOperation();
}
void finished( object sender, EventArgs e )
{
if( this.InvokeRequired ) {
this.BeginInvoke( (LongOperationFinishedEventHandler)finished, sender, e );
return;
}
// You can safely modify the gui here.
}

Categories