How can I cancel a thread? - vb.net

I do something in a thread. But sometimes I don't want to wait till all pings are finished.
How can I cancel a thread?
Can you show me please the code?
Private Sub Start_Button_Click(sender As Object, e As EventArgs) Handles Start_Button.Click
DoSomething()
End Sub
Private Sub Cancel_Button_Click(sender As Object, e As EventArgs) Handles Cancel_Button.Click
THRD.Cancel '<-- Thread cancel!??!???
End Sub
Sub DoSomething()
Dim THRD As New Thread(Sub()
Dim IPArea As String = "192.168.1."
Dim LastIP As Integer
For LastIP = 0 To 255
Dim TestIP As String = IPArea & CStr(LastIP)
If My.Computer.Network.Ping(TestIP, 10) Then
ListBox1.Items.Add(TestIP)
End If
Next
End Sub)
THRD.IsBackground = True
THRD.Start()
End Sub

Here is my working solution and this solution is only to show how to move the THRD as a form level variable to allow stopping it when clicking the cancel button. I added some validations to prevent exceptions.
Public Class Form1
Private THRD As Threading.Thread
Private Sub Start_Button_Click(sender As Object, e As EventArgs) Handles Start_Button.Click
DoSomething()
End Sub
Private Sub Cancel_Button_Click(sender As Object, e As EventArgs) Handles Cancel_Button.Click
If THRD IsNot Nothing AndAlso THRD.IsAlive Then
THRD.Abort() '<-- Thread cancel!??!???
THRD = Nothing
AddToList("Stopped.")
Else
AddToList("Thread not running.")
End If
End Sub
Sub DoSomething()
If THRD IsNot Nothing AndAlso THRD.IsAlive Then
AddToList("Still working...")
Exit Sub
End If
THRD = New Threading.Thread(Sub()
Dim IPArea As String = "192.168.1."
Dim LastIP As Integer
For LastIP = 0 To 255
Dim TestIP As String = IPArea & CStr(LastIP)
If My.Computer.Network.Ping(TestIP, 10) Then
AddToList(TestIP)
End If
Next
AddToList("Done")
End Sub)
THRD.IsBackground = True
THRD.Start()
End Sub
''' <summary>
''' Thead-safe add value to list.
''' </summary>
''' <param name="value">The value.</param>
Private Sub AddToList(value As String)
If ListBox1.InvokeRequired Then
ListBox1.Invoke(Sub() ListBox1.Items.Add(value))
Else
ListBox1.Items.Add(value)
End If
End Sub
End Class

Related

How to track multiple BackgroundworkerX.Runworkercompleted operations

I am trying to use a single handler to cover the end of multiple backgroundworker activities and cannot find a way to get the information about the specific backgroundworker using the backgroundworkercompleted event.
My code to catch the event is as below:
Private Sub BGx_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted, BackgroundWorker2.RunWorkerCompleted, BackgroundWorker3.RunWorkerCompleted, BackgroundWorker4.RunWorkerCompleted, BackgroundWorker5.RunWorkerCompleted, BackgroundWorker6.RunWorkerCompleted, BackgroundWorker7.RunWorkerCompleted, BackgroundWorker8.RunWorkerCompleted
'Do work here based on completed Backgroundworker
For BG = 1 To 8
If Not DSWorkers(BG).IsBusy Then
If DStatus(BG) = -2 Then : DStatus(BG) = -1 : End If
End If
Next
Complete()
End Sub
There is nothing on the "Do Work Here" section because I do not know how to capture and have been unable to find details of the backgroundworkercompleted event id.
Please - any pointers as to how I can identify the specific completed BackgroundWorker
As with all event handlers, the sender parameter is a reference to the object that raised the event, so you can access the actual BackgroundWorker that has completed its work via that. If you need some data other than that, you assign it to the e.Result property in the DoWork event handler and get it back from the e.Result property in the RunWorkerCompleted event handler. e.Result works for getting data out of the DoWork event handler much as e.Argument works for getting data in.
Check this out for some examples of using BackgroundWorker objects, including passing data out using e.Result. You might also like to checkout my own BackgroundMultiWorker class, which basically combines the functionality of multiple BackgroundWorker objects into a single BackgroundMultiWorker object. It identifies each task using a token.
EDIT:
Here's an example that may help with this issue and your task in general:
Imports System.ComponentModel
Imports System.Threading
Public Class Form1
Private ReadOnly resultsByWorker As New Dictionary(Of BackgroundWorker, BackgroundWorkerResult)
Private ReadOnly rng As New Random
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'The NumericUpDown is used to select the index of a BackgroundWorker to cancel.
With NumericUpDown1
.DecimalPlaces = 0
.Minimum = 0
.Maximum = 9
End With
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'Create 10 BackgroundWorkers and run them.
For i = 1 To 10
Dim worker As New BackgroundWorker
resultsByWorker.Add(worker, New BackgroundWorkerResult)
AddHandler worker.DoWork, AddressOf workers_DoWork
AddHandler worker.RunWorkerCompleted, AddressOf workers_RunWorkerCompleted
worker.WorkerSupportsCancellation = True
worker.RunWorkerAsync()
Next
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim index = Convert.ToInt32(NumericUpDown1.Value)
Dim worker = resultsByWorker.Keys.ToArray()(index)
If worker.IsBusy Then
'Cancel the BackgroundWorker at the specified index.
worker.CancelAsync()
End If
End Sub
Private Sub workers_DoWork(sender As Object, e As DoWorkEventArgs)
Dim worker = DirectCast(sender, BackgroundWorker)
'Do work for a random number of seconds between 10 and 20.
Dim period = rng.Next(10, 20 + 1)
For i = 0 To period
If worker.CancellationPending Then
e.Cancel = True
Return
End If
'Simulate work.
Thread.Sleep(1000)
Next
'The work was completed without being cancelled.
e.Result = period
End Sub
Private Sub workers_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs)
Dim worker = DirectCast(sender, BackgroundWorker)
Dim result = resultsByWorker(worker)
If e.Cancelled Then
result.WasCancelled = True
Else
result.Result = CInt(e.Result)
End If
Dim workers = resultsByWorker.Keys.ToArray()
If Not workers.Any(Function(bgw) bgw.IsBusy) Then
'All work has completed so display the results.
Dim results As New List(Of String)
For i = 0 To workers.GetUpperBound(0)
worker = workers(i)
result = resultsByWorker(worker)
results.Add($"Worker {i} {If(result.WasCancelled, "was cancelled", $"completed {result.Result} iterations")}.")
Next
MessageBox.Show(String.Join(Environment.NewLine, results))
End If
End Sub
End Class
Public Class BackgroundWorkerResult
Public Property WasCancelled As Boolean
Public Property Result As Integer
End Class
Here is that example reworked to use a single instance of the BackgroundMultiWorker is linked to instead of multiple instances of the BackgroundWorker class.
Imports System.Threading
Public Class Form1
Private WithEvents worker As New BackgroundMultiWorker With {.WorkerSupportsCancellation = True}
Private ReadOnly results(9) As BackgroundWorkerResult
Private ReadOnly rng As New Random
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'The NumericUpDown is used to select the index of a BackgroundWorker to cancel.
With NumericUpDown1
.DecimalPlaces = 0
.Minimum = 0
.Maximum = results.GetUpperBound(0)
End With
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'Create 10 BackgroundWorkers and run them.
For i = 0 To results.GetUpperBound(0)
results(i) = New BackgroundWorkerResult
worker.RunWorkerAsync(i)
Next
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim index = Convert.ToInt32(NumericUpDown1.Value)
If worker.IsBusy(index) Then
'Cancel the BackgroundWorker at the specified index.
worker.CancelAsync(index)
End If
End Sub
Private Sub worker_DoWork(sender As Object, e As DoWorkEventArgs) Handles worker.DoWork
'Do work for a random number of seconds between 10 and 20.
Dim period = rng.Next(10, 20 + 1)
For i = 0 To period
If worker.IsCancellationPending(e.Token) Then
e.Cancel = True
Return
End If
'Simulate work.
Thread.Sleep(1000)
Next
'The work was completed without being cancelled.
e.Result = period
End Sub
Private Sub workers_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles worker.RunWorkerCompleted
Dim result = results(CInt(e.Token))
If e.Cancelled Then
result.WasCancelled = True
Else
result.Result = CInt(e.Result)
End If
If Not worker.IsBusy() Then
'All work has completed so display the results.
Dim output As New List(Of String)
For i = 0 To results.GetUpperBound(0)
result = results(i)
output.Add($"Task {i} {If(result.WasCancelled, "was cancelled", $"completed {result.Result} iterations")}.")
Next
MessageBox.Show(String.Join(Environment.NewLine, output))
End If
End Sub
End Class
Public Class BackgroundWorkerResult
Public Property WasCancelled As Boolean
Public Property Result As Integer
End Class

Updating progress bar from threadpool

Im trying to update a progress bar in my main form from a threadpool but everything Iv'e tried so far updates the value of the progress bar but not the physical appearance of it, Tried several different ways and still can't find a solution. Can someone please point me in the right direction to update the progress bar on my form1 by 1 every time a thread has completed.
Imports System.Threading
Public Class Form1
Dim arry As New List(Of String)
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Try
BackgroundWorker1.RunWorkerAsync()
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim ii As Integer = 2000
For a As Integer = 0 To ii
arry.Add(a)
Next
ProgressBar1.Maximum = arry.Count
ThreadPool.SetMaxThreads(4, 4)
Dim doneEvents(arry.Count) As ManualResetEvent
Dim r As New Random()
For i As Integer = 0 To arry.Count
doneEvents(i) = New ManualResetEvent(False)
Dim f = New stuff(r.Next(20, 40), doneEvents(i))
ThreadPool.QueueUserWorkItem(AddressOf f.ThreadPoolCallBack, i)
Next
For Each handle As WaitHandle In doneEvents
handle.WaitOne()
Next
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
CheckForIllegalCrossThreadCalls = False
End Sub
Private Delegate Sub FillDelegate()
Public Sub Fill()
If ProgressBar1.InvokeRequired Then
ProgressBar1.BeginInvoke(New FillDelegate(AddressOf Fill))
Else
ProgressBar1.Increment(1)
ProgressBar1.Refresh()
End If
End Sub
End Class
Public Class stuff
Private _doneEvent As ManualResetEvent
Private _n As Integer
Public ReadOnly Property N() As Integer
Get
Return _n
End Get
End Property
Sub New(ByVal n As Integer, ByVal doneEvent As ManualResetEvent)
_n = n
_doneEvent = doneEvent
End Sub
Public Sub ThreadPoolCallBack(ByVal threadContext As Object)
Try
Dim threadIndex As Integer = CType(threadContext, Integer)
Console.WriteLine("thread {0} started...", threadIndex)
Form1.Fill()
Console.WriteLine("thread {0} finished...", threadIndex)
Catch ex As Exception
Console.WriteLine("error in threadPoolCallback")
Console.WriteLine(ex.Message)
Finally
_doneEvent.Set()
End Try
End Sub
End Class

Random delay in seconds from numericupdown in VB.Net

I have a desktop winforms app code:
Sub Delay(ByVal dblSecs As Double)
Const OneSec As Double = 1.0# / (1440.0# * 60.0#)
Dim dblWaitTil As Date
Now.AddSeconds(OneSec)
dblWaitTil = Now.AddSeconds(OneSec).AddSeconds(dblSecs)
Do Until Now > dblWaitTil
Application.DoEvents()
Loop
End Sub
Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.Click
Webbrowser1.Navigate(TextBox1.Text)
Delay(Val(DelayText.Text))
end sub
What I need is to set max. delay, same from textbox by entering seconds.
I need this to be random delay number so Im stacking here, thanks for correcting my code.
Also If its possible to make it in NumericUpDown, as i found some topics which says textbox text property is different as NumericUpDown but i like it more.
You could use an inbetween class which does most of the work for you
This class would take the WebBrowser, attach to some events of it, and would refresh periodically (depending on MinimumWait / MaximumWait)
As it is using threading, it also checks if the usercontrol needs to be invoked to Refresh it and when yes, invokes the custom refresh delegate
Public Class Refresher
Protected Delegate Sub RefreshNavigationDelegate(browser As WebBrowser)
Protected Sub RefreshNavigation(browser As WebBrowser)
If browser.InvokeRequired Then
browser.Invoke(New RefreshNavigationDelegate(AddressOf RefreshNavigation), browser)
Return
End If
browser.Refresh(WebBrowserRefreshOption.Completely)
End Sub
Private _isBusy As Boolean = False
Public Property IsBusy As Boolean
Get
Return _isBusy
End Get
Protected Set(value As Boolean)
If _isBusy = value Then
Return
End If
_isBusy = value
End Set
End Property
Public Property MinimumWait As Integer = 2000
Public Property MaximumWait As Integer = 10000
Private refreshThread As Thread = Nothing
Private _browser As WebBrowser
Public Property Browser As WebBrowser
Get
Return _browser
End Get
Set(value As WebBrowser)
If Object.Equals(_browser, value) Then
Return
End If
StopRefresh()
If _browser IsNot Nothing Then
RemoveHandler Browser.DocumentCompleted, AddressOf DocumentComplete
RemoveHandler Browser.Navigating, AddressOf Navigating
End If
_browser = value
If _browser IsNot Nothing Then
AddHandler Browser.DocumentCompleted, AddressOf DocumentComplete
AddHandler Browser.Navigating, AddressOf Navigating
AddHandler Browser.ProgressChanged, AddressOf ProgressChanged
End If
StartRefresh()
End Set
End Property
Protected Sub ProgressChanged(sender As Object, e As WebBrowserProgressChangedEventArgs)
IsBusy = e.CurrentProgress > 0 AndAlso e.CurrentProgress < e.MaximumProgress
End Sub
Protected Sub DocumentComplete(sender As Object, e As WebBrowserDocumentCompletedEventArgs)
IsBusy = False
End Sub
Protected Sub Navigating(sender As Object, e As WebBrowserNavigatingEventArgs)
IsBusy = True
End Sub
Public Sub StartRefresh()
If refreshThread IsNot Nothing Then
Return
End If
refreshThread = New Thread(AddressOf DoRandomRefreshes)
refreshThread.Start()
End Sub
Public Sub StopRefresh()
If refreshThread Is Nothing Then
Return
End If
refreshThread.Abort()
refreshThread = Nothing
End Sub
Protected Overridable Sub DoRandomRefreshes()
Dim randomGenerator As New Random()
While Not refreshThread.ThreadState = ThreadState.AbortRequested
Dim newTimeout As Integer = MinimumWait + randomGenerator.Next(MaximumWait - MinimumWait)
Thread.Sleep(newTimeout)
If Not IsBusy Then
RefreshNavigation(Browser)
End If
End While
End Sub
Public Sub New()
End Sub
End Class
You could then use it in your form as such:
Public Class Form1
Dim myRefresher As Refresher = New Refresher()
Private Sub tsbGo_Click(sender As Object, e As EventArgs) Handles tsbGo.Click
WebBrowser1.Navigate(txtUrl.Text)
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
myRefresher.Browser = WebBrowser1
End Sub
Private Sub txtMin_TextChanged(sender As Object, e As EventArgs) Handles txtMin.TextChanged
Dim int As Integer = 0
If Integer.TryParse(txtMin.Text, int) Then
myRefresher.MinimumWait = int
End If
End Sub
Private Sub txtMax_TextChanged(sender As Object, e As EventArgs) Handles txtMax.TextChanged
Dim int As Integer = 0
If Integer.TryParse(txtMax.Text, int) Then
myRefresher.MaximumWait = int
End If
End Sub
End Class

How to get threading to work?

Here is the code:
Imports System.Threading
Partial Public Class frmTermsOfUse
Inherits Form
Public Sub New()
InitializeComponent()
rtxtContent.Text = My.Resources.TermsOfUse
End Sub
Private Shared [exit] As Boolean = True
Private Sub btnAccept_Click(sender As Object, e As EventArgs)
XMLSettings.WriteValue("ShowToU", (Not chkDontShowAgain.Checked).ToString())
[exit] = False
Me.Close()
End Sub
Private Sub frmTermsOfUse_Load(sender As Object, e As EventArgs)
lblToU.Left = (Me.Width \ 2) - (lblToU.Width / 2)
Dim t As New Thread(AddressOf Wait20Sec)
t.Start()
End Sub
Private Sub Wait20Sec()
For i As Integer = 19 To 0 Step -1
System.Threading.Thread.Sleep(1000)
Try
Me.Invoke(DirectCast(Sub() btnAccept.Text = "Accept (" & i & ")", MethodInvoker))
Catch
End Try
Next
Me.Invoke(DirectCast(Sub()
btnAccept.Text = "Accept"
btnAccept.Enabled = True
End Sub, MethodInvoker))
End Sub
Private Sub btnDecline_Click(sender As Object, e As EventArgs)
System.Diagnostics.Process.GetCurrentProcess().Kill()
End Sub
Private Sub frmTermsOfUse_FormClosing(sender As Object, e As FormClosingEventArgs)
If e.CloseReason = CloseReason.UserClosing AndAlso [exit] Then
System.Diagnostics.Process.GetCurrentProcess().Kill()
End If
End Sub
End Class
I don't get the countdown on the button to work.
Button is disabled when I launch the application and does nothing else.
How can I fix this?
Already thanks for the help

vb2012 textbox backcolor does't change using thread

I wrote very simple thread example.
Make normal form1 and drop 1 textbox
run thread work on form load
thread change a textbox backcolor looks like switch on/off
But, it doesn't work.....
Can you tell me why doesn't work??
see my source.
=====================================================================
Imports System.Threading
Public Class Monitor
Public wObj As Worker = New Worker()
Public MyThread As Thread = New Thread(AddressOf wObj.DoWork)
Public Sub ChangeTBColor(pOption As Integer)
If pOption = 1 Then
tb1.BackColor = Color.Aqua
Else
tb1.BackColor = Color.Red
End If
End Sub
Private Sub Monitor_Load(sender As Object, e As EventArgs) Handles Me.Load
MyThread.Start()
Console.WriteLine("Running OrgThread..")
Console.WriteLine("Stop running")
End Sub
Private Sub BtnThreadStop_Click(sender As Object, e As EventArgs) Handles BtnThreadStop.Click
Me.wObj.RequestStop()
End Sub
End Class
Public Class Worker
Private LoopStop As Boolean = True
Public Sub DoWork()
Console.WriteLine("User Thread Start!")
Dim iTemp As Integer = 0
While (LoopStop)
Monitor.ChangeTBColor(iTemp Mod 2)
iTemp = iTemp + 1
Thread.Sleep(500)
End While
Console.WriteLine("User Thread End.!")
End Sub
Public Sub RequestStop()
LoopStop = False
End Sub
End Class
As already mentioned, your starting a new thread for your work, the issue is your trying to change the color for a control that need invoking. With this said, we need a delegate for when the control needs to be invoked... In my example, I used one class that handles this all and works great. Also please make sure to import System.ComponentModel because this is needed for the BackgroundWorker... I used the background worker as it takes all the heavy lifting off you would need...
Imports System.ComponentModel
Imports System.Threading
Public Class Monitor
Delegate Sub SetColor(ByVal clr As Integer) 'Your delegate..
Private WithEvents bw As New BackgroundWorker
Public Sub ChangeTBColor(pOption As Integer)
If Me.tb1.InvokeRequired Then 'Invoke if required...
Dim d As New SetColor(AddressOf ChangeTBColor) 'Your delegate...
Me.Invoke(d, New Object() {pOption})
Else
If pOption = 1 Then
tb1.BackColor = Color.Aqua
Else
tb1.BackColor = Color.Red
End If
End If
End Sub
Private Sub Monitor_Load(sender As Object, e As EventArgs) Handles Me.Load
bw.WorkerSupportsCancellation = True
Console.WriteLine("Running OrgThread..")
bw.RunWorkerAsync()
End Sub
Private Sub bw_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles bw.DoWork
Dim iTemp As Integer = 0
Dim LoopStop As Boolean = True
Console.WriteLine("User Thread Start!")
While (LoopStop)
If Not (bw.CancellationPending) Then
ChangeTBColor(iTemp Mod 2)
iTemp = iTemp + 1
Thread.Sleep(500)
Else
e.Cancel = True
LoopStop = False
End If
End While
End Sub
Private Sub bw_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bw.RunWorkerCompleted
Console.WriteLine("User Thread End.!")
End Sub
Private Sub BtnThreadStop_Click(sender As Object, e As EventArgs) Handles BtnThreadStop.Click
If bw.IsBusy Then
bw.CancelAsync()
Else
Console.WriteLine("Running OrgThread..")
bw.RunWorkerAsync()
End If
End Sub
End Class
Here's my screenshot of my test... This is tried and tested. Please be sure to vote if this helps you!