By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
424,985 Members | 1,885 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 424,985 IT Pros & Developers. It's quick & easy.

RunWorkerAsync using UI input value

100+
P: 116
Hi

I am running a background worker that continually updates a list. The interval is determined by a NumericUpDown on the UI.

If I alter the interval whilst the background is working I get a cross-threading error.

I understand I can send an argument when starting the background worker in the format RunWorkerAsync(arg) but could someone just clarify how to use that?

Do I need to set a separate class for the argument value or can I just use RunWorkerAsync(myInterval.value) and then get the value as e.argument in the DoWork?

In short, how do I get the following idea to work?

Expand|Select|Wrap|Line Numbers
  1. BackgroundWorker1.RunWorkerAsync(myInterval.value)
  2.  
  3. Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
  4. Dim checkcount As Integer = 0
  5. Do checkcount += 1
  6. BackgroundWorker1.ReportProgress(checkcount)
  7. Threading.Thread.Sleep(e.Argument * 1000)
  8. If BackgroundWorker1.CancellationPending = True Then Exit Do
  9. Loop
  10. End Sub
  11.  
Thanks
Nov 26 '14 #1

✓ answered by Frinavale

You don't need a class to change the value but you do need a variable that is accessible to all threads.

In my example, the variable is an integer and because it is a native type/not a reference type I do not need to use SyncLock on the item to prevent cross threading issues. Please note that any variables that are Objects (classes) will require you to uses a SyncLock block around them to prevent issues where threads are trying to update the same value at the same time. Also be aware that you could result in a deadlock if too many threads are going at the same time and all of them are SyncLocking the common variables.

Anyways, here's an example!

I have created a WPF project which has one window in it called MainWindow. On the MainWindow I have defined:
  • a ComboBox used to set the sleep timer value and also used to stop the background work if 0 is selected
  • a TextBlock (a control similar to a win form's label) that is used to display results that the background work will produce

Here is the XML code for the window:
Expand|Select|Wrap|Line Numbers
  1. <Window x:Class="MainWindow"
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.         Title="MainWindow" Height="350" Width="525"
  5.         xmlns:local="clr-namespace:PracticeProject">
  6.     <Grid>
  7.         <Grid.RowDefinitions>
  8.             <RowDefinition Height="Auto"/>
  9.             <RowDefinition Height="Auto" />
  10.         </Grid.RowDefinitions>
  11.         <TextBlock x:Name="StatusMessage" Text="" Grid.Row="0" />
  12.         <ComboBox x:Name="MyInterval" Grid.Row="1" 
  13.                           SelectedIndex="0"
  14.                           SelectionChanged="MyInterval_SelectionChanged">
  15.             <ComboBox.Items>
  16.                 <ComboBoxItem>0</ComboBoxItem>
  17.                 <ComboBoxItem>5</ComboBoxItem>
  18.                 <ComboBoxItem>8</ComboBoxItem>
  19.                 <ComboBoxItem>10</ComboBoxItem>
  20.             </ComboBox.Items>
  21.         </ComboBox>
  22.     </Grid>
  23. </Window>
In the VB.NET code for the MainWindow I have 2 private members defined: a background worker, the value selected that is shared across all background worker threads to indicate the interval that should be used.

I have a method that handles the ComboBox's SelectionChanged event.
In that method I:
  • set the selected interval value
  • create a new background worker if one hasn't been created and begin the process
  • stop the background worker if the value selected is 0 (please note that the process will be stopped as soon as the current sleep timer elapses and the thread detects the cancellation)

I have a Do Work method that does the same thing as your method but also calls a methods to print things to the screen so that I can demonstrate what is going on and I have a completed method that is similar to yours as well.

Here is the VB.NET code:
Expand|Select|Wrap|Line Numbers
  1. Class MainWindow
  2.     Private Delegate Sub ErrorCreateSource(ByVal msg As String)
  3.     Private waitingBackgroundWorker As System.ComponentModel.BackgroundWorker = Nothing ' used to do the work
  4.     Private valueSelected As Integer ' indicates the sleep interval value selected
  5.  
  6.     Private Sub MyInterval_SelectionChanged(sender As System.Object, e As System.Windows.Controls.SelectionChangedEventArgs)
  7.         'Here I would have to use a SyncLock if I wasn't using an Integer type 
  8.         'to pass information to the background worker process in order to prevent multiple things 
  9.         'from setting the value in the middle of processing
  10.  
  11.         'Attempting to parse the item selected into the valueSelected variable (if I can't I stop the process)
  12.         'And checking to make sure the value is greater than 0 (my stop case is when I select 0)
  13.         If Integer.TryParse(DirectCast(DirectCast(sender, ComboBox).SelectedItem, ComboBoxItem).Content, valueSelected) AndAlso valueSelected > 0 Then
  14.  
  15.             If waitingBackgroundWorker Is Nothing Then
  16.                 ' we do not have a background worker yet: creating one and starting it
  17.                 waitingBackgroundWorker = New System.ComponentModel.BackgroundWorker
  18.                 waitingBackgroundWorker.WorkerReportsProgress = True
  19.                 waitingBackgroundWorker.WorkerSupportsCancellation = True
  20.                 AddHandler waitingBackgroundWorker.DoWork, AddressOf WaitBackgroundWorker_DoWork 'the method that is used to do the work
  21.                 AddHandler waitingBackgroundWorker.RunWorkerCompleted, AddressOf WaitBackgroundWorker_RunWorkerCompleted ' the method that is used when work is completed
  22.                 waitingBackgroundWorker.RunWorkerAsync() 'staring the process
  23.             End If
  24.         Else
  25.             'Either the values selected was not a number or the value selected was 0 
  26.             If waitingBackgroundWorker IsNot Nothing Then
  27.                 waitingBackgroundWorker.CancelAsync()
  28.             End If
  29.         End If
  30.     End Sub
  31.     Private Sub WaitBackgroundWorker_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs)
  32.         Dim checkcount As Integer = 0
  33.         Dim oldTimer = valueSelected
  34.         Do
  35.             checkcount += 1
  36.             sender.ReportProgress(checkcount)
  37.             System.Threading.Thread.Sleep(valueSelected * 1000)
  38.             If sender.CancellationPending = True Then Exit Do
  39.             UpdateUI(String.Format("Continuing: {0}old timer value: {1} {0}new timer value: {2} {0}iteration: {3}", System.Environment.NewLine, oldTimer.ToString, valueSelected.ToString, checkcount.ToString))
  40.         Loop
  41.         e.Result = String.Format("{0}Reults: {0}old timer value: {1} {0}new timer value: {2} {0}iteration: {3}", System.Environment.NewLine, oldTimer.ToString, valueSelected.ToString, checkcount.ToString)
  42.     End Sub
  43.     Private Sub WaitBackgroundWorker_RunWorkerCompleted(ByVal sender As Object, ByVal e As ComponentModel.RunWorkerCompletedEventArgs)
  44.         UpdateUI(e.Result)
  45.         UpdateUI(String.Format("Completed! {0} ", e.Result))
  46.         CType(sender, System.ComponentModel.BackgroundWorker).Dispose()
  47.         waitingBackgroundWorker = Nothing
  48.     End Sub
  49.     Sub UpdateUI(ByVal msg As String)
  50.         Dim uiHandler As New ErrorCreateSource(AddressOf UpdateUIIndicatorsMsg)
  51.         Me.Dispatcher.Invoke(Windows.Threading.DispatcherPriority.Normal, uiHandler, msg)
  52.     End Sub
  53.     Sub UpdateUIIndicatorsMsg(ByVal msg As String)
  54.         StatusMessage.Text = msg
  55.     End Sub
  56. End Class
I think the code is self explanatory, but if you have questions please ask!

-Frinny

Share this Question
Share on Google+
3 Replies


Frinavale
Expert Mod 5K+
P: 9,731
You don't need a class to change the value but you do need a variable that is accessible to all threads.

In my example, the variable is an integer and because it is a native type/not a reference type I do not need to use SyncLock on the item to prevent cross threading issues. Please note that any variables that are Objects (classes) will require you to uses a SyncLock block around them to prevent issues where threads are trying to update the same value at the same time. Also be aware that you could result in a deadlock if too many threads are going at the same time and all of them are SyncLocking the common variables.

Anyways, here's an example!

I have created a WPF project which has one window in it called MainWindow. On the MainWindow I have defined:
  • a ComboBox used to set the sleep timer value and also used to stop the background work if 0 is selected
  • a TextBlock (a control similar to a win form's label) that is used to display results that the background work will produce

Here is the XML code for the window:
Expand|Select|Wrap|Line Numbers
  1. <Window x:Class="MainWindow"
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.         Title="MainWindow" Height="350" Width="525"
  5.         xmlns:local="clr-namespace:PracticeProject">
  6.     <Grid>
  7.         <Grid.RowDefinitions>
  8.             <RowDefinition Height="Auto"/>
  9.             <RowDefinition Height="Auto" />
  10.         </Grid.RowDefinitions>
  11.         <TextBlock x:Name="StatusMessage" Text="" Grid.Row="0" />
  12.         <ComboBox x:Name="MyInterval" Grid.Row="1" 
  13.                           SelectedIndex="0"
  14.                           SelectionChanged="MyInterval_SelectionChanged">
  15.             <ComboBox.Items>
  16.                 <ComboBoxItem>0</ComboBoxItem>
  17.                 <ComboBoxItem>5</ComboBoxItem>
  18.                 <ComboBoxItem>8</ComboBoxItem>
  19.                 <ComboBoxItem>10</ComboBoxItem>
  20.             </ComboBox.Items>
  21.         </ComboBox>
  22.     </Grid>
  23. </Window>
In the VB.NET code for the MainWindow I have 2 private members defined: a background worker, the value selected that is shared across all background worker threads to indicate the interval that should be used.

I have a method that handles the ComboBox's SelectionChanged event.
In that method I:
  • set the selected interval value
  • create a new background worker if one hasn't been created and begin the process
  • stop the background worker if the value selected is 0 (please note that the process will be stopped as soon as the current sleep timer elapses and the thread detects the cancellation)

I have a Do Work method that does the same thing as your method but also calls a methods to print things to the screen so that I can demonstrate what is going on and I have a completed method that is similar to yours as well.

Here is the VB.NET code:
Expand|Select|Wrap|Line Numbers
  1. Class MainWindow
  2.     Private Delegate Sub ErrorCreateSource(ByVal msg As String)
  3.     Private waitingBackgroundWorker As System.ComponentModel.BackgroundWorker = Nothing ' used to do the work
  4.     Private valueSelected As Integer ' indicates the sleep interval value selected
  5.  
  6.     Private Sub MyInterval_SelectionChanged(sender As System.Object, e As System.Windows.Controls.SelectionChangedEventArgs)
  7.         'Here I would have to use a SyncLock if I wasn't using an Integer type 
  8.         'to pass information to the background worker process in order to prevent multiple things 
  9.         'from setting the value in the middle of processing
  10.  
  11.         'Attempting to parse the item selected into the valueSelected variable (if I can't I stop the process)
  12.         'And checking to make sure the value is greater than 0 (my stop case is when I select 0)
  13.         If Integer.TryParse(DirectCast(DirectCast(sender, ComboBox).SelectedItem, ComboBoxItem).Content, valueSelected) AndAlso valueSelected > 0 Then
  14.  
  15.             If waitingBackgroundWorker Is Nothing Then
  16.                 ' we do not have a background worker yet: creating one and starting it
  17.                 waitingBackgroundWorker = New System.ComponentModel.BackgroundWorker
  18.                 waitingBackgroundWorker.WorkerReportsProgress = True
  19.                 waitingBackgroundWorker.WorkerSupportsCancellation = True
  20.                 AddHandler waitingBackgroundWorker.DoWork, AddressOf WaitBackgroundWorker_DoWork 'the method that is used to do the work
  21.                 AddHandler waitingBackgroundWorker.RunWorkerCompleted, AddressOf WaitBackgroundWorker_RunWorkerCompleted ' the method that is used when work is completed
  22.                 waitingBackgroundWorker.RunWorkerAsync() 'staring the process
  23.             End If
  24.         Else
  25.             'Either the values selected was not a number or the value selected was 0 
  26.             If waitingBackgroundWorker IsNot Nothing Then
  27.                 waitingBackgroundWorker.CancelAsync()
  28.             End If
  29.         End If
  30.     End Sub
  31.     Private Sub WaitBackgroundWorker_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs)
  32.         Dim checkcount As Integer = 0
  33.         Dim oldTimer = valueSelected
  34.         Do
  35.             checkcount += 1
  36.             sender.ReportProgress(checkcount)
  37.             System.Threading.Thread.Sleep(valueSelected * 1000)
  38.             If sender.CancellationPending = True Then Exit Do
  39.             UpdateUI(String.Format("Continuing: {0}old timer value: {1} {0}new timer value: {2} {0}iteration: {3}", System.Environment.NewLine, oldTimer.ToString, valueSelected.ToString, checkcount.ToString))
  40.         Loop
  41.         e.Result = String.Format("{0}Reults: {0}old timer value: {1} {0}new timer value: {2} {0}iteration: {3}", System.Environment.NewLine, oldTimer.ToString, valueSelected.ToString, checkcount.ToString)
  42.     End Sub
  43.     Private Sub WaitBackgroundWorker_RunWorkerCompleted(ByVal sender As Object, ByVal e As ComponentModel.RunWorkerCompletedEventArgs)
  44.         UpdateUI(e.Result)
  45.         UpdateUI(String.Format("Completed! {0} ", e.Result))
  46.         CType(sender, System.ComponentModel.BackgroundWorker).Dispose()
  47.         waitingBackgroundWorker = Nothing
  48.     End Sub
  49.     Sub UpdateUI(ByVal msg As String)
  50.         Dim uiHandler As New ErrorCreateSource(AddressOf UpdateUIIndicatorsMsg)
  51.         Me.Dispatcher.Invoke(Windows.Threading.DispatcherPriority.Normal, uiHandler, msg)
  52.     End Sub
  53.     Sub UpdateUIIndicatorsMsg(ByVal msg As String)
  54.         StatusMessage.Text = msg
  55.     End Sub
  56. End Class
I think the code is self explanatory, but if you have questions please ask!

-Frinny
Nov 26 '14 #2

100+
P: 116
Many thanks for the extensive reply Frinny.

I'll try to work through it and see if I can follow it all. It seems one major difference is the fact that the valueSelected is declared differently and not just using the numericUpDown value.

I'll see how I get on and let you know. Thanks again.
Nov 27 '14 #3

100+
P: 116
Some very useful pointers in your reply Frinny.

I've had a go with my code and it seems the suggestion to have the numericUpDown value stored in a separate variable works just fine.

I'm still fairly new to VB so things like this don't necessarily occur to me but seem fairly obvious when pointed out!

In this instance the Worker reporting back to the UI seems ok without additional code but I'll certainly check back to your post if get into difficulties elsewhere.

Many thanks again.
Nov 27 '14 #4

Post your reply

Sign in to post your reply or Sign up for a free account.