I need to perform 2 tasks in parallel. One will load data in the GUI, till then I want to run a progress bar continuously in front of user. I tried BackgroundWorker but it is giving me some Thread synchronization error. Can somebody suggest me any other best way of doing same.
Code:
backgroundWorker1 initialization:
backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
if (backgroundWorker1.IsBusy != true)
{
backgroundWorker1.RunWorkerAsync();
}
error coming on following line:
XmlDocumentHierarchy _remoteObj = new XmlDocumentHierarchy(comboBox2.Text, "username", "password");
is:
"Cross-thread operation not valid: Control 'comboBox2' accessed from a thread other than the thread it was created on."
You are trying access comboBox2.Text in thread other than GUI thread (background worker thread). If you using only one property in background worker thread, than you can pass `comboBox2.Text' to background worker method:
if (backgroundWorker1.IsBusy != true)
{
backgroundWorker1.RunWorkerAsync(comboBox2.Text);
}
In backgroundWorker1_DoWork procedure you can read property in following way:
void backgroundWorker1_DoWork(Object sender, DoWorkEventArgs e)
{
String comboBoxText = (String)e.Argument;
XmlDocumentHierarchy _remoteObj = new XmlDocumentHierarchy(comboBoxText, "username", "password");
}
If you accessing more than one property from GUI controls you can create simple class to pass all necessary data to your background worker method.
If you need to access GUI thread from BackgroundWorker thread, you can easily invoke your methods in the GUI thread like this:
public Form1()
{
InitializeComponent();
Thread thr = new Thread(new ThreadStart(BackGroundThread));
thr.Start();
}
void BackGroundThread()
{
for (int i = 0; i < 100; i++)
{
// The line below will be run in the GUI thread with no synchronization issues
BeginInvoke((Action)delegate { this.Text = "Processed " + i.ToString() + "%"; });
Thread.Sleep(200);
}
}
Related
I am creating a WPF application to collect logs. I am using different Backgroundworker to execute some scripts. Worker1 for script1, Worker2 for script2 and so on. When Backgroundworker completes RunworkerCompleted method get executed.
So, how can I wait for all the workers to complete? Once all these workers are complete, I need to update the UI thread.
You can try using Tasks.
Task t1 = Task.Run(() =>
{
// Do something
}
);
Task t2 = Task.Run(() =>
{
// Do something
}
);
Task t3 = Task.Run(() =>
{
// Do something
}
);
await Task.WhenAll(new Task[] { t1, t2, t3 }).ContinueWith((task) =>
{
// Do something when all three tasks have completed...
}
);
Here is a way to wait for all BackgroundWorker objects to get complete. I hope you are using C#:
static void Main(string[] args)
{
int noOfbackgroundWorker = 5;
WaitHandle[] waitHandles = new WaitHandle[noOfbackgroundWorker];
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < noOfbackgroundWorker; i++)
{
var bg1 = new BackgroundWorker();
var handle = new EventWaitHandle(false, EventResetMode.ManualReset);
bg1.DoWork += delegate(object sender, DoWorkEventArgs e) { Thread.Sleep(i * 1000); };
bg1.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e) { handle.Set(); };
waitHandles[i] = handle;
bg1.RunWorkerAsync();
}
WaitHandle.WaitAll(waitHandles);
Console.WriteLine("Done in "+sw.Elapsed.Seconds + " seconds");
Console.ReadKey();
}
Apart from above if there is low no of BackgroundWorker, just set a bool variable for each worker to map each worker complete status and call a common method from all RunworkerCompleted event handler that just has condition if(a && b && c.....) if passed then notify the UI.
As I say in my Task.Run vs BackgroundWorker conclusion:
anything like waiting for two separate background operations to complete before doing something else is much easier with Task.Run. Pretty much any time you have to coordinate background operations, Task.Run code is going to be much simpler!
(this is the conclusion of a series of blog posts comparing Task.Run to BackgroundWorker).
Worker1 for script1, Worker2 for script2 and so on.
You can do this with Task.Run:
Result DoWork(Script script) { ... }
IEnumerable<Script> scripts = ...;
var tasks = scripts.Select(x => Task.Run(() => DoWork(x))).ToList();
So, how can I wait for all the workers to complete? Once all these workers are complete, I need to update the UI thread.
Use await and Task.WhenAll:
var results = await Task.WhenAll(tasks);
... // Update UI with results
this is my first question in this forum, hope it will not be duplicated somewhere because i have searched for the respons for almost 4 weeks without making any progress.
here is my situation,
im developing an application that need to do a lot of background operation, for that reason i creat 2 BKW, the first one used to load data from a DB and put it inside an observable collection , 'no need to report progress or support cancelation for this one' :
private Boolean loadTestSteps()
{
// Create a background worker thread that don't report progress and does not
// support cancelation
BackgroundWorker wk_LoadTestSteps = new BackgroundWorker();
wk_LoadTestSteps.DoWork += new DoWorkEventHandler(wk_LoadTestSteps_DoWork);
wk_LoadTestSteps.RunWorkerAsync();
return true;
}
observable collection class :
public class clsTestStep : DependencyObject
{
public static DependencyProperty TestStepProperty = DependencyProperty.Register(
"TestStep", typeof(String), typeof(clsTestStep));
public string TestStep
{
get { return (string)GetValue(TestStepProperty); }
set { SetValue(TestStepProperty, value); }
} and so on for the rest of items....
now the main backGround that should do the longer operation and in the same time report the progress to the main UI ,declared like so
private void InitializeBackGroundWork()
{
_wk_StartTest = new BackgroundWorker();
// Create a background worker thread that ReportsProgress &
// SupportsCancellation
// Hook up the appropriate events.
_wk_StartTest.DoWork += new DoWorkEventHandler(_wk_StartTest_DoWork);
_wk_StartTest.ProgressChanged += new ProgressChangedEventHandler
(_wk_StartTest_ProgressChanged);
_wk_StartTest.RunWorkerCompleted += new RunWorkerCompletedEventHandler
(_wk_StartTest_RunWorkerCompleted);
_wk_StartTest.WorkerReportsProgress = true;
_wk_StartTest.WorkerSupportsCancellation = true;
_wk_StartTest.RunWorkerAsync();
}
in the do work events, exactly in the foreach loop i encontered an error saying : you cannot access this object because another thread own it :
void _wk_StartTest_DoWork(object sender, DoWorkEventArgs e)
{
//Loop through each test step and perform Test
foreach (clsTestStep item in _testStep)
{
Thread.Sleep(200);
temp[0] = item.TestStep;
temp[1] = item.Delay.ToString();
temp[2] = item.NumberRepetition.ToString();
temp[3] = item.Mode.ToString();
//Report % of Progress, Test step Name,and the paragraph from Class PerformTest
_wk_StartTest.ReportProgress(counter,
temp[0]);
counter += 1;
_performTest.Fdispatcher(temp, out _paragraph);
//_si.PgBarMax = Convert.ToDouble(_testStep.Count);
}
//Report completion on operation completed
_wk_StartTest.ReportProgress(counter);
}
what im missing here please, because my head is gonna explod from searching !!!
It sounds like your ObservableCollection is created and so owned by an other thread so your _wk_StartTest_DoWork method can't access it.
Where your _testStep variable comes from ?
By the way, in a multithread environment when many thread access the same data you should prefer the use of ConcurrentBag class instead of an ObservableCollection. ConcurrentBag is thread safe.
for the ones that may enconter this kind of problem ^^
finnaly i have found a way to acess class even if its not owned by the current thread here a nice article explaining step by step how to do this here
I have a code like the following (I have stripped some code for readability)
private void RicercaArticoloStripped(object sender, DoWorkEventArgs e)
{
try
{
using (var context = new ControlloSchedeLocalEntities())
{
var prodotti = context.VProdotti.Where(i => i.WACMAT == textBoxCodiceArticolo.Text);
if (prodotti.Count() > 0)
{
this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(delegate()
{
textBlockDescrizioneArticolo.Text = prodotti.FirstOrDefault().WADESC;
}));
}
}
}
catch (Exception ex)
{
MessageBox.Show("Error:\n\n" + ex.Message + "\r\nStack: " + ex.ToString());
}
}
private void textBoxCodiceArticolo_KeyUpStripped(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += new DoWorkEventHandler(RicercaArticoloStripped);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RicercaArticoloWorkerCompleted);
worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
object[] parameters = new object[] { textBoxCodiceArticolo.Text, textBlockDescrizioneArticolo.Text };
worker.RunWorkerAsync(parameters);
}
}
So, as for my understanding of BackgroundWorker, when I instantiate the:
var prodotti = context.VProdotti.Where(i => i.WACMAT == textBoxCodiceArticolo.Text);
I am doing it FROM the working thread, not the UI thread.
But I get an exception on the line immediately below, when I try to access the value:
if (prodotti.Count() > 0)
I get the (in)famous error:
"The calling thread cannot access this object because a different thread owns it"
Why?
As you already said you must use Dispatcher.BeginInvoke/Invoke to perform operations from the control's owner thread or you will get "The calling thread cannot access this object because a different thread owns it" exception. Thats why you got this exception;
And here is why you got this exception on the line below (when prodotti.Count() was called):
When you create prodotti variable it's just a IEnumerable<T> object. So he actually calculates only when you call for prodotti.Count() method and thats why you got exception on this line.
IEnumerable actually is generator, that means that he will produce new set of objects every time he used.
To test this you can calculate prodotti as shown bellow:
var prodotti = context.VProdotti.Where(i => i.WACMAT == textBoxCodiceArticolo.Text).ToList();
In this case you will get exception immediately because .ToList() forces all calculations.
Check this article for generators and enumerators: http://www.codeproject.com/Articles/155462/IEnumerable-Lazy-and-Dangerous
Updated:
really, when you use IEnumerable with reference types you can get the same objects as previously. Read this answer for more: https://stackoverflow.com/a/14361094/1467309
The property Text of TextBox gets the value of the DependencyProperty Text, this can only be done from the UI-thread. You are accessing textBoxCodiceArticolo.Text from your Worker thread.
I recently rewrote my Windows Forms application to use BackgroundWorker instances instead of using manually created "worker threads". After checkin I noticed some tests started to fail. After some debugging I can demonstate my problems by showing you the following 2 tests:
[Test]
public void Test_A()
{
bool progressChanged = false;
var worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += (s, e) => worker.ReportProgress(0, null);
worker.ProgressChanged += (s, e) => progressChanged = true;
worker.RunWorkerAsync();
Thread.Sleep(100);
progressChanged.ShouldBeTrue();
}
[Test]
public void Test_B()
{
//Creation of o form component causes (?) this test to fail, even do I dispose it
var view = new Form();
view.Dispose();
bool progressChanged = false;
var worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += (s, e) => worker.ReportProgress(0, null);
worker.ProgressChanged += (s, e) => progressChanged = true;
worker.RunWorkerAsync();
Thread.Sleep(100);
progressChanged.ShouldBeTrue();
}
Test_A success while Test_B fails. This is regardless if I sleep 100 ms or 100 minutes. Why?? My production code seem to work though, but it is annoying not being able to have a regression test suite that works (I have other test creating Forms components and these tests must be executed BEFORE my test using BackgroundWorker)
The next step would be to examine the source code of BackgroundWorker, but before I do that I thought I`d check for help here.
Regards Michael
Add
WindowsFormsSynchronizationContext.AutoInstall = false;
In Test_B() before anything else. The BackgroundWorker, wich is very UI (ie: Winforms) oriented, is wild guessing how to sync things, but not like you want. Have a look here for some explanation (especially from Scott Berry): WindowsFormsSynchronizationContext
I need in wpf app check messages on server. I have own method which load messages on server-LoadRp().
I would like to create some kind of listener which would check, every 3 seconds whether on the server are not new messages.
I call method for loading messages on dispatcher timer tick event, it is suitable? Any another solution. It’s possible call timer in another thread in wpf?
Code is here:
public MessangerWindow(PokecCommands pokecCmd)
{
InitializeComponent();
PokecCmd = pokecCmd;
_friendsData = PokecCmd.LoadFriends();
friendsListBox.DataContext = _friendsData;
_dispatcherTimer = new DispatcherTimer();
_dispatcherTimer.Tick+=new EventHandler(DispatcherTimer_Tick);
_dispatcherTimer.Interval = new TimeSpan(0,0,3);
_dispatcherTimer.Start();
}
private void DispatcherTimer_Tick(object sender, EventArgs eventArgs)
{
try
{
//try load new message from sever
RP message = PokecCmd.LoadRp();
//arived message
if (message != null)
{
//exist window
if (_chatWindows.ContainsKey(message.Nick))
{
_chatWindows[message.Nick].Show();
}
{
//create new Window
var chatWindow = new ChatWindow(PokecCmd, message);
_chatWindows.Add(message.Nick, chatWindow);
chatWindow.Show();
}
}
}
catch (Exception ex)
{
//MessageBox.Show(ex.Message);
}
}
What is suitable to use:
Dispatcher with no background threads
Dispatcher with background threads
Multiple Threads
If you are ok with locking up your UI for the time it takes to check on the server, using a DispatcherTimer the way you are doing it will work fine.
If checking for new messages could take more than a few milliseconds and you want your UI to be responsive while it checks, you should use multiple threads. In that case, once the new data had arrived you would use Dispatcher.Invoke to display it.
Your code in the thread that checks for messages might look like this:
//try load new message from sever
RP message = PokecCmd.LoadRp();
//arived message
if( message != null )
Dispatcher.Invoke(DispatcherPriority.Send, new Action(() =>
{
//exist window
if (_chatWindows.ContainsKey(message.Nick))
{
_chatWindows[message.Nick].Show();
}
{
//create new Window
var chatWindow = new ChatWindow(PokecCmd, message);
_chatWindows.Add(message.Nick, chatWindow);
chatWindow.Show();
}
}
);