How to CORRECTLY implement a multithreaded progressbar during a LINQ query? - vb.net

Okay so here's the thing:
I have a linq query which loads approx. 1000 lines into a variable, during that process I want to display a progressbar, not necessarily stating the percentage, can be marquee style, doesnt matter.
This progressbar is on a modal form to precent the user from interacting with the app for the time the query's running.
Now here's my code:
Private Sub LoadBar()
Try
Dim load As New frmLoadbar
load.Text = "Loading bunch of data..."
load.ShowDialog()
Catch e As Threading.ThreadAbortException
Threading.Thread.ResetAbort()
End Try
End Sub
In another sub:
Dim myThreadDelegate As New Threading.ThreadStart(AddressOf LoadBar)
Dim th As New Threading.Thread(myThreadDelegate)
th.Name = "TimeConsuming"
th.Start()
Dim XY = db.Table.GetEnumerator
While XY.MoveNext
Dim item As New ListViewItem
item.Text = XY.Current.Name
item.Tag = XY.Current
ListBox1.Items.Add(item)
End While
Autos.Dispose()
Try
th.Abort()
Catch ex As Exception //here's where i 'swallow the re-thrown exception
End Try
Not thats one of the ugliest code i've ever written.It works i just dont want that rethrown exception.
Some explanation:
I want the modal form to close after the query is done.
For that reason I 'abort' the thread running the form.
Since aborting a thread throws a double-exception i have to 'swallow'
that exception.
Now i know i could implement this like the following:
Coding a loop into the form holding the progressbar, which checks
periodically for a boolean's value, and if its true the form could
close itself.
From the other form - on the worker thread - i could change that
booleans value to true after the query's finished.
But here comes my question:
Whats the best way to implement this?
I know it can be done with a background worker, which has been
specifically invented for this reason, but can i use the background
worker as the thread to show the progressbar?
If not (and i have to run the query on the background worker and
showing the modal form from my original form), would that mean that
the query would "work in the background"?
Would that mean that the query would be slower?
I've looked into other tutorials, but for one reason or another, either i wasnt able to copy it (due to complexity) or I wasn't convinced that it was better than this.
Thank you for your time you took to answer.

You could show the modal form and then run a BackgroundWorker from that form. The progress and completed events would be on the UI thread so you can update a progress bar while it is running and close the form in the completed event handler.

Okay, so for future reference, if someone needs clear help with code samples, Microsoft has it (thats a first..)
You can download it here:
Multithreading

Assuming Windows Forms, you do this with a BackgroundWorker component.

Related

In VB.NET use a textbox as a log for which if statement it is beeing proccesed inside a sub

Hi i have a Sub that has multiple if statements in it.
Each if statement has a large loop that searches for specific files and text inside files.
I tried various ways to use a text box in order to get the information which if is currently proccessing at the time and i see that for some reason the ui is not refreshed until the sub finishes and so i see everytime in the textbox the last proceesed if message.
What do you think is the best way to handle it?
I hope that this has nothing to do with threads because threads are something that i am not familiar with !
I think using Application.DoEvents() is an easy choice. But I don't know if that would be the desired behavior.
If the use of Application.DoEvents() fails, another thread should handle it.
https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.application.doevents?view=netframework-3.5
I use this:
Public Sub logWithCrLf(tx As TextBox, s As String)
tx.AppendText(s & vbCrLf)
tx.Select(tx.TextLength - 1, 0)
tx.ScrollToCaret()
tx.Refresh()
End Sub
I see that it scrolls a bit smoother using tx.AppendText(s) than tx.Text &= s, which scrolls up to 0 then down to caret again.
(I write this as an answer to contribute with the tx.AppendText() recommendation)

Multithreading Webbrowsers

I am currently making a vb program that i plan to make very big. I have a decent knowledge of visual basic but today i came across something i do not understand. Because of the huge size of my program , i decided to try and keep the program as organized as possible by putting specific subs in modules. These subs consist of httprequest , webbrowsers(control), webclients and alot of loops. In order to prevent these subs from lagging my main application i thread them using threading.thread and i start them from my main form. But this leads to two problems.
Problem 1: The threads cannot in any way interact with the main form.
Once the a httprequest or webclient collects the information from my desired website, i am trying to make it add the info to a listbox in my main form, So what i did is it typed
Msgbox("Info Sent")
form1.listbox1.items.add(String)
The first messagebox will show but although the code right under it runs, nothing is added to the first forms listbox.I am not using delegates to transfer the information, instead, although its not a good habit, i am using checkforillegalcrossovers.
Problem 2: Threading with a webbrowser.
Threading with a webbrowser using threading.thread also does not work because it causes an active x error. After looking it up i found that a solution was to use a single threaded apartment but this would not work because i may need multiple threads running off the same sub at once.
One solution that i have found to this problem is creating another form completely and setting it invisible, and since the form is its own thread i do not need to use threading.thread , but the problem comes when i am trying to create multiple threads, or else i can somehow dynamically create the threads and put the subs inside of it programically this method wont work And even if it does i feel that it is sloppy so i will leave this for one of two last resorts.
The other solution is the most simple one in which i just put all of the code in the main form, but if i keep on doing that form1 is gonna become huge and sloppy, doing this wont solve the webbrowser problem either and even when using regions i still feel that something that 1000+ lines deserves its own class.
There must be some solution out there that solves these problems. Any help would be appreciated, Thanks.
I checked my code for updating the progress bar, and using a single thread with synclock will NOT work. They way I make it work is perform the step of the pbar each time after a thread is started as I have limited total threads (say less than 5 threads). Thus, even the progress bar steps before the threads are finished, but it will not progress further before new threads started. It is not 100% accurate but it more or less telling the progress
'update the progress bar
some_form.PBar1.PerformStep()
' This while loop is to count the existing running thread,
' and determine whether new thread should start
While 1
Dim t2 = New System.Threading.Thread(Sub() WaitForPermission())
t2.Start()
t2.Join()
If proceed_gen Then
Exit While
End If
End While
'Start doing what I need to do
Dim t1 = SomeSub()
t1.Start()
'End of code, as VB doest not have thread.detach()
Correct me if I am wrong, but you probably have to use a background worker. I know this is annoying, but this is the limitation of VB.net.
Or, you can have something like this (pseudo code, not tested)
structure some_struct
'define the strings you want to update, and their status such that
'main() knows if you need to update the stuff to the form
' You can also put how many threads are running, and the status of each thread,
'such that the main knows if all threads are completed
end structure
sub your_sub()
'Manipulate the website, and update the data structure with
'proper stuff you need
end sub
sub main(){
dim t1 = New System.Threading.Thread(Sub() your_sub())
t1.start()
' I am listing only one threads here, but start as many as you want
'check if there are strings that you need to update to the form
while 1
'check if there are any stuff you want to update from the data structure.
' Make sure you use synclock on the data structure, so each thread won't fight each other on accessing the data struct
dim should_update as boolean = false
should_update = 'Something thatyou should implement to judge whether you should update the form.
'You can start a thread and join it so the thread won't fight with other threads for accessing the data structure
dim some_string as string
if should_update
some_string = 'You may also need a thread to join to get the stuff you need. Dim the string as an array if it is required.
'You can also try pass by ref
'if you need to use thread to access the data structure to know if you need to update the form
form1.listbox1.items.add(some_string )
end if
end while
end sub
This is an ugly solution, but it will help you do the job...

i written a winforms application in VB .NET in visual studio 2010

I have to run a thread create in the code.
In the form1 i have a button that run the new separate thread for elaborate some data, so i need it for not freeze the form.
I have inizialized thread:
dim th as thread = new thread (addressof elaborate)
And at the button.click event:
th.isbackground= true
th.start()
Now, at the form load i have iconized my program, but when i start new thread the tray icon is duplicated from it.
I want to resolve that when start new thread it's not show new notifyicon.
Any ideas?
(i don't have found anything online, only Multiple notification icons appear when using multithreading)
Create a class called Elab
inside that class, put a sub called work
Add a timer to your form that is disabled
with a tickcount of say 1000
Declare this in your form class:
Dim El as Elab
inside Form_Load() put:
El = New Elab()
Under your button, put this:
Dim gThread as new System.Threading.Thread(Address of El.Work)
Timer1.Enabled = True
Inside Elab declare a variable called Result:
Public Result as boolean
When elab has finished whatever it is doing, set result as true, and store the results in public variables you can access later.
Inside the timer:
If El.Result = True then
'Get results, deal with data
end if
This isn't written particularly well, and isn't a working example, but is to mearly point you in the right direction, by giving a thread an address of a sub inside a class, you can then access the same class from other threads, which means your form doesn't freeze, and you're not creating a new instance of your form, you are just accessing an existing classes sub routine; just make sure to give yourself a way to get the results (in this example i suggested a timer, but a "get result" button would do the same job) once the thread has completed.
Remeber:
If you need Elab to finish before a particular part of code can continue (for example, elab might add two numbers, and you need the result to continue) you can start the thread and do this:
Do until El.Result = True
Application.DoEvents()
System.Threading.Thread.Sleep(1)
Loop
gThread.Join()
Hope this helps a little.
So the answer to your question is; don't put your sub inside a form, instead put it in a class that you can create an instance of.

VB.net Messages Pumped to Label

I basically want to implement a Label and have it constantly showing information to the user. I want to be able to do something like this:
someMethod():
printMessage("Starting program")
doWork() //possibly does some calls to printMessage()
printMessage("Finished program")
end
printMessage(string message)
mylabel.Text += message
end
And have the label on a Windows Form constantly be showing that output. That is, instead of the user having to wait until someMethod() is finished and having all the info suddenly dumped on to the label, I want it to be printed to the label as the information comes out.
I tried looking at threading to solve this problem, and I have it working using code something like:
someMethod():
Dim t As New Thread(AddressOf printMessage)
t.Start("Starting program")
doWork()
printMessage("Finished program")
end
(And there is a delegate for printMessage and inside I check the InvokedRequired property of mylabel) But with this, I keep getting all the information just suddenly dumped on to the label, and the order is no longer preserved. I may get output like:
"some other data from doWork()"
"Finished program"
"Starting program"
So any ideas how I can accomplish this?
Thanks.
You might want to consider using a BackgroundWorker. It will make it easy for you to have the work done on a separate thread and still report progress back to the UI thread.
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
And have the rest of the app remain responsive.
The BackGroundWorker has a DoWork event where you could put code that looked (roughly) like this:
BGW.ReportProgress(0, "Starting Task1")
DoTask1()
BGW.ReportProgress(0, "Completed Task1")
BGW.ReportProgress(0, "Starting Task2")
DoTask2()
BGW.ReportProgress(0, "Completed Task2")
You'd also handle the ProgressChanged event which will fire (on the thread that started the worker) to update the label.
A call to Application.DoEvents() may be beneficial, as it forces the window to update.
Try placing it right after each of your printMessage calls.

VB.NET - Interrupt form loop and end form

I have a form that goes through an endless loop and processes data. When I click a button that "closes" the form, the form keeps processing even though it is closed. I want the form to completely end and exit out of its loop statement, and then open a new form.
Here is the code I am using to close the form
frmMain.Close()
frmMain.Dispose()
Note: I am not using threads it's just a simple VB.NET application. I am not closing the main startup form.
The "correct" way of doing this is with background worker threads really. But this will also work without the need of background worker threads.
Declare a variable in the form class.
Private keepLoopAlive As Boolean
Then write your processing loop to be something like:
keepLoopAlive = True
Do While keepLoopAlive
(your code that loops here)
DoEvents
Loop
Then on your Close event do:
keepLoopAlive = False
Me.Close()
This will cause the loop to end first chance it gets, and your form should close.
Please note I've written this code from memory and not in an IDE so there may be typos.
I am not a .NET developer so this may not be valid, but if I were performing an infinite loop, I would be checking each time I started that the loop was still valid with some sort of boolean value, and if it failed, drop out of the loop.
When you close the form, set the boolean value false and it will drop out, and you can either have an outer loop that waits and restarts the loop, or you can restart the entire function some other time.