Why doesn't this multithreaded VB.NET (2010 Express Edition) program work properly? - vb.net

I'm trying to make a program of mine into a multithreaded application, but I've hit a pair of snags that I documented in the following code. Any help that I can get with this to make it behave properly would be greatly appreciated so I can expand this stub into a more efficient version of my existing application.
Thank you for any advice you have on this matter.
- Aaron
Imports System.Threading
Public Class frmMain
''' <summary>Initializes the multithreaded form</summary>
Private Sub Initialize() Handles MyBase.Load
AddThread(AddressOf Update_UI)
running = True
For Each Thread In ThreadPool
Thread.IsBackground = True
Thread.Start()
Next
End Sub
''' <summary>Terminates the multithreaded form</summary>
Protected Overrides Sub Finalize() Handles MyBase.FormClosing
running = False
For Each Thread In ThreadPool
Thread.Join()
Thread = Nothing
Next
End Sub
''' <summary>Adds a worker thread to the ThreadPool</summary>
''' <param name="pointer">The AddressOf the function to run on a new thread.</param>
Private Sub AddThread(ByRef pointer As System.Threading.ParameterizedThreadStart)
Dim newthread As Integer
If ThreadPool Is Nothing Then newthread = 0 Else newthread = ThreadPool.GetUpperBound(0) + 1
ReDim Preserve ThreadPool(newthread)
ThreadPool(newthread) = New Thread(pointer)
End Sub
''' <summary>Updates the User Interface</summary>
Private Sub Update_UI()
'HELP: The commented out lines in this subroutine make the program work incorrectly when uncommented.
'HELP: It should echo 'output' to the titlebar of frmMain, but it also makes the form unresponsive.
'HELP: When I force the form to quit, the 'termination alert' does not trigger, instead the application hangs completely on Thread.Join (see above).
'HELP: If I remove DoEvents(), the form is unable to be closed...it simply goes unresponsive. Shouldn't the multithreading keep us from needing DoEvents()?
'If Me.InvokeRequired Then
' Me.Invoke(New MethodInvoker(AddressOf Update_UI))
'Else
While running
Dim output As String = System.DateTime.Now + " :: Update_UI() is active!"
Debug.Print(output)
'Application.DoEvents()
'Me.Text = output
End While
Debug.Print(System.DateTime.Now + " :: Termination signal recieved...")
'End If
End Sub
Delegate Sub dlgUpdate_UI()
Private ThreadPool() As Thread
Private running As Boolean
End Class

Yes, none of what you tried can work properly. You correctly identified the need to use Control.Invoke() to run the Me.Text assignment on the main thread. This is what is going wrong:
Your Invoke() call makes the entire method run on the main thread. It will start executing the loop and never exit. Your form goes catatonic since it can't do anything else anymore, like repaint the caption bar to show the changed text or respond to user input
The DoEvents call makes the form come back alive but now you've got a new problem: the user can close the window and your code keeps running. The running flag will never be set to false so the program won't stop. The user interface is gone though. Code would normally bomb on an ObjectDisposedException but not in your specific case, the Text property is stored in a private variable
You could alter the code so that only the Me.Text assignment runs on the main thread. But now you've got a new problem: the main thread will get pummeled by invoke request and doesn't get around to doing its regular (low priority) duties anymore. It goes catatonic. The essential problem is that you are trying to update the caption bar way too fast. There's no point, the user cannot read that fast. Update 20 times per second is plenty and looks smooth to the human eye
Do not use the Finalize() method for tasks like this, the code can easily trigger the 2 second finalizer thread time-out, bombing your program.
Do consider using the BackgroundWorker class, it takes care of some of these details.

It's the do while loop that is burning up all your cycles so you are loosing the battle and keeping the procesor busy no matter how many threads you use. Something like the following will be better suited for what you are trying to achieve.
Imports System.Threading
Public Class Form1
Private t As New Timer(AddressOf DoTimer, Nothing, 1000, 1000)
Private Sub DoTimer(ByVal state As Object)
UpdateUi()
End Sub
''' <summary>Updates the User Interface</summary>
Private Sub UpdateUi()
If InvokeRequired Then
Invoke(New DlgUpdateUi(AddressOf UpdateUi))
Else
Dim output As String = DateTime.Now & " :: Update_UI() is active!"
Debug.Print(output)
Text = output
End If
End Sub
Delegate Sub DlgUpdateUi()
Private Sub Form1_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
t.Dispose()
End Sub
End Class

If I have said once I have said it a million times. Using Invoke, while useful in many situations, is abused and way overused. If all you want to do is have the progress of the worker threads displayed to a user then using Invoke is not always the best option. And it does not look like the best option here either.
Instead, publish the status text you are assigning to output into a variable that can be accessed via the UI thread. Then use a System.Windows.Forms.Timer to periodically poll its value at a more reasonable rate...maybe every 1 second or so. The Tick event already runs on the UI thread so you can immediately begin using this value to display to the end user by manipulating the various UI controls.
Strings are really easy to pass around from thread to thread because they are immutable which means they are inherently thread-safe. The only thing you really have to worry about is making sure the UI thread sees the most recent reference published to the shared variable. In C# you would use the volatile keyword for this. In VB you can use Thread.VolatileRead from the UI thread and Thread.VolatileWrite from the worker thread. Of course, if you are more comfortable wrapping the reads and writes in a SyncLock that is perfectly acceptable as well.

Related

How can I run code in a background thread and still access the UI?

I made a file search program in visual studio on windows 10 using .net lang,
My problem starts from form1 with a "dim frm2 as form2 = new form2" call,
after the new form being shown i start a while loop on form1 that feeds data into a listbox in form 2:
1)form1 call form2 and show it.
2)form1 start a while loop.
3)inside the while loop data being fed to listbox1 in frm2
Now everything works on windows 10, the while loop can run as much as it needs without any trouble, the window can loose focus and regain focus without showing any "Not Responding.." msgs or white\black screens..
But, when i take the software to my friend computer which is running windows 7, install all required frameworks and visual studio itself, run it from the .sln in debug mode, and do the same search on the same folder the results are:
1) the while loop runs smoothly as long as form 2 dont loose focus
(something that doesnt happen on windows 10)
2) when i click anywhere on the screen the software loose focus what
causes 1) to happen (black screen\white screen\not responding etc..)
3) if i wait the time needed for the loop and dont click anywhere else
it keeps running smoohtly, updating a label like it should with the
amount of files found.. and even finish the loop with 100% success
(again unless i click somewhere)
Code Example:
Sub ScanButtonInForm1()
Dim frm2 As Form2 = New Form2
frm2.Show()
Dim AlreadyScanned As HashSet(Of String) = New HashSet(Of String)
Dim stack As New Stack(Of String)
stack.Push("...Directoy To Start The Search From...")
Do While (stack.Count > 0)
frm2.Label4.Text = "-- Mapping Files... -- Folders Left:" + stack.Count.ToString + " -- Files Found:" + frm2.ListBox1.Items.Count.ToString + " --"
frm2.Label4.Refresh()
Dim ScanDir As String = stack.Pop
If AlreadyScanned.Add(ScanDir) Then
Try
Try
Try
Dim directoryName As String
For Each directoryName In System.IO.Directory.GetDirectories(ScanDir)
stack.Push(directoryName)
frm2.Label4.Text = "-- Mapping Files... -- Folders Left:" + stack.Count.ToString + " -- Files Found:" + frm2.ListBox1.Items.Count.ToString + " --"
frm2.Label4.Refresh()
Next
frm2.ListBox1.Items.AddRange(System.IO.Directory.GetFiles(ScanDir, "*.*", System.IO.SearchOption.AllDirectories))
Catch ex5 As UnauthorizedAccessException
End Try
Catch ex2 As System.IO.PathTooLongException
End Try
Catch ex4 As System.IO.DirectoryNotFoundException
End Try
End If
Loop
End Sub
My conclusions was simple!
1) windows 7 dont support live ui (label) update from a while loop
called from a button...
2) windows 7 could possibly support a new
thread running the same loop
i think mabye if i run all the code in a thread mabye the ui will remain responsive
(by the way the UI is not responsive in windows 10 but i still see
the label refresh and nothing crashes when form loose focus..)
so i know how to do that but i also know that if i do that a thread will not be able to update a listbox or a label in a form and refresh it..
so the thread will need to update an external file with the data and the form2 will need to read that data live from the file but will it make the same problems? i have no idea what to do.. can use some help and tips. THANK YOU!
I must menttion the fact that the loop is working on windows 10 without a responsive UI means i cant click on any button but i can
still see the label refresh BUT on windows 7 everything works the same
UNLESS i click somewhere, no matter where i click on windows the loop
crashes
im using framework 4.6.2 developer
While I'm glad you found a solution, I advise against using Application.DoEvents() because it is bad practice.
Please see this blog post: Keeping your UI Responsive and the Dangers of Application.DoEvents.
Simply put, Application.DoEvents() is a dirty workaround that makes your UI seem responsive because it forces the UI thread to handle all currently available window messages. WM_PAINT is one of those messages which is why your window redraws.
However this has some backsides to it... For instance:
If you were to close the form during this "background" process it would most likely throw an error.
Another backside is that if the ScanButtonInForm1() method is called by the click of a button you'd be able to click that button again (unless you set Enabled = False) and starting the process once more, which brings us to yet another backside:
The more Application.DoEvents()-loops you start the more you occupy the UI thread, which will cause your CPU usage to rise rather quickly. Since every loop is run in the same thread your processor cannot schedule the work over different cores nor threads, so your code will always run on one core, eating as much CPU as possible.
The replacement is, of course, proper multithreading (or the Task Parallel Library, whichever you prefer). Regular multithreading actually isn't that hard to implement.
The basics
In order to create a new thread you only need to declare an instance of the Thread class and pass a delegate to the method you want the thread to run:
Dim myThread As New Thread(AddressOf <your method here>)
...then you should set its IsBackground property to True if you want it to close automatically when the program closes (otherwise it keeps the program open until the thread finishes).
Then you just call Start() and you have a running background thread!
Dim myThread As New Thread(AddressOf myThreadMethod)
myThread.IsBackground = True
myThread.Start()
Accessing the UI thread
The tricky part about multithreading is to marshal calls to the UI thread. A background thread generally cannot access elements (controls) on the UI thread because that might cause concurrency issues (two threads accessing the same control at the same time). Therefore you must marshal your calls to the UI by scheduling them for execution on the UI thread itself. That way you will no longer have the risk of concurrency because all UI related code is run on the UI thread.
To marhsal calls to the UI thread you use either of the Control.Invoke() or Control.BeginInvoke() methods. BeginInvoke() is the asynchronous version, which means it doesn't wait for the UI call to complete before it lets the background thread continue with its work.
One should also make sure to check the Control.InvokeRequired property, which tells you if you already are on the UI thread (in which case invoking is extremely unnecessary) or not.
The basic InvokeRequired/Invoke pattern looks like this (mostly for reference, keep reading below for shorter ways):
'This delegate will be used to tell Control.Invoke() which method we want to invoke on the UI thread.
Private Delegate Sub UpdateTextBoxDelegate(ByVal TargetTextBox As TextBox, ByVal Text As String)
Private Sub myThreadMethod() 'The method that our thread runs.
'Do some background stuff...
If Me.InvokeRequired = True Then '"Me" being the current form.
Me.Invoke(New UpdateTextBoxDelegate(AddressOf UpdateTextBox), TextBox1, "Status update!") 'We are in a background thread, therefore we must invoke.
Else
UpdateTextBox(TextBox1, "Status update!") 'We are on the UI thread, no invoking required.
End If
'Do some more background stuff...
End Sub
'This is the method that Control.Invoke() will execute.
Private Sub UpdateTextBox(ByVal TargetTextBox As TextBox, ByVal Text As String)
TargetTextBox.Text = Text
End Sub
New UpdateTextBoxDelegate(AddressOf UpdateTextBox) creates a new instance of the UpdateTextBoxDelegate that points to our UpdateTextBox method (the method to invoke on the UI).
However as of Visual Basic 2010 (10.0) and above you can use Lambda expressions which makes invoking much easier:
Private Sub myThreadMethod()
'Do some background stuff...
If Me.InvokeRequired = True Then '"Me" being the current form.
Me.Invoke(Sub() TextBox1.Text = "Status update!") 'We are in a background thread, therefore we must invoke.
Else
TextBox1.Text = "Status update!" 'We are on the UI thread, no invoking required.
End If
'Do some more background stuff...
End Sub
Now all you have to do is type Sub() and then continue typing code like if you were in a regular method:
If Me.InvokeRequired = True Then
Me.Invoke(Sub()
TextBox1.Text = "Status update!"
Me.Text = "Hello world!"
Label1.Location = New Point(128, 32)
ProgressBar1.Value += 1
End Sub)
Else
TextBox1.Text = "Status update!"
Me.Text = "Hello world!"
Label1.Location = New Point(128, 32)
ProgressBar1.Value += 1
End If
And that's how you marshal calls to the UI thread!
Making it simpler
To make it even more simple to invoke to the UI you can create an Extension method that does the invoking and InvokeRequired check for you.
Place this in a separate code file:
Imports System.Runtime.CompilerServices
Public Module Extensions
''' <summary>
''' Invokes the specified method on the calling control's thread (if necessary, otherwise on the current thread).
''' </summary>
''' <param name="Control">The control which's thread to invoke the method at.</param>
''' <param name="Method">The method to invoke.</param>
''' <param name="Parameters">The parameters to pass to the method (optional).</param>
''' <remarks></remarks>
<Extension()> _
Public Function InvokeIfRequired(ByVal Control As Control, ByVal Method As [Delegate], ByVal ParamArray Parameters As Object()) As Object
If Parameters IsNot Nothing AndAlso _
Parameters.Length = 0 Then Parameters = Nothing
If Control.InvokeRequired = True Then
Return Control.Invoke(Method, Parameters)
Else
Return Method.DynamicInvoke(Parameters)
End If
End Function
End Module
Now you only need to call this single method when you want to access the UI, no additional If-Then-Else required:
Private Sub myThreadMethod()
'Do some background stuff...
Me.InvokeIfRequired(Sub()
TextBox1.Text = "Status update!"
Me.Text = "Hello world!"
Label1.Location = New Point(128, 32)
End Sub)
'Do some more background stuff...
End Sub
Returning objects/data from the UI with InvokeIfRequired()
With my InvokeIfRequired() extension method you can also return objects or data from the UI thread in a simple manner. For instance if you want the width of a label:
Dim LabelWidth As Integer = Me.InvokeIfRequired(Function() Label1.Width)
Example
The following code will increment a counter that tells you for how long the thread has run:
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim CounterThread As New Thread(AddressOf CounterThreadMethod)
CounterThread.IsBackground = True
CounterThread.Start()
Button1.Enabled = False 'Make the button unclickable (so that we cannot start yet another thread).
End Sub
Private Sub CounterThreadMethod()
Dim Time As Integer = 0
While True
Thread.Sleep(1000) 'Wait for approximately 1000 ms (1 second).
Time += 1
Me.InvokeIfRequired(Sub() Label1.Text = "Thread has been running for: " & Time & " seconds.")
End While
End Sub
Hope this helps!
The reason your application is freezing is that you are doing all the work on the UI thread. Check out Async and Await. It uses threading in the background but makes it way easier to manage. An example here:
https://stephenhaunts.com/2014/10/14/using-async-and-await-to-update-the-ui-thread/

Multi-threading: dynamically create threads, call WS and report back for X number of tasks

I'm new to multi-threading, so I need a general guidance how to proceed.
In a nutshell, I need to call an external webservice thousands of times per second. The response is roughly 1 second per record, which does not work well if I have a million of records to send. So, I've been tasked to use multi-threading to open multiple threads (the number is controlled dynamically) to simultaneously call WS. So, if I can open 100 threads that call WS simultaneously, the task should finish much faster... in theory.
Code is at the bottom, I've cut a lot of unnecessary pieces out of it so please let me know if something makes no sense
The idea behind this code is you would call in Threads.Process, pass DataTable with data that needs to be sent out via WS. This process would run until we processed all records in the data table and _threads (which holds a list of background objects currently working) has zero items in it.
StartThreads would instantiate new background objects if number of simultaneous threads permits and there is new data to be worked on. When a background object completes its task, in order to pass data back to the static object to log its data, it calls Threads.ThreadFinished and passes itself as parameter at which point logging info would be recorded and backgroundobject is removed from _threads.
However, during my testing I noticed that threads are overlaping when calling Threads.ThreadFinished, I tried to offset it with SyncLock to keep it thread safe, but it still does not work 100% fool proof (probably because there are other shared functions still not protected). Since I don't understand this threading very well, I have a feeling there is an easier way of doing this. So, am I on a right track? Should I keep going or switch to a different method? I've researched other multi-threading methods like ThreadPool, but I ended up with this method.
Public Class Threads
Public Shared Sub ProcessRecords(ByVal dt As DataTable)
_threads.Clear()
_startTime = Now
_dt = dt
While Working()
StartThreads()
Thread.Sleep(100)
End While
End Sub
Private Shared Sub StartThreads()
While _threads.Count < Settings.NumberOfThreads AndAlso _rowCounter < _dt.Rows.Count
Dim id As String = GetRandomChar(, 20) ' Generate a random ID for logging purposes
Dim tw As New ThreadWorker(_dt.Rows(_rowCounter), _rowCounter, id, _dt.Rows(_rowCounter)("id").ToString())
_rowCounter += 1
_threads.Add(tw)
End While
End Sub
Public Shared Sub ThreadFinished(ByVal tw As ThreadWorker)
SyncLock tw
Log(tw.Log)
_threads.Remove(tw)
End SyncLock
StartThreads()
End Sub
Private Shared Function Working() As Boolean
Return _rowCounter < _dt.Rows.Count OrElse _threads.Count > 0
End Function
End Class
Public Class ThreadWorker
Private _bw As System.ComponentModel.BackgroundWorker
Private _logDebug As New StringBuilder
Sub New(ByVal dr As DataRow)
_bw = New System.ComponentModel.BackgroundWorker
_bw.WorkerReportsProgress = True
_bw.WorkerSupportsCancellation = True
AddHandler _bw.DoWork, AddressOf bw_Working
AddHandler _bw.RunWorkerCompleted, AddressOf bw_RunWorkerCompleted
_bw.RunWorkerAsync()
End Sub
Private Sub bw_Working(ByVal Sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs)
' Call webservice ...
_logDebug.AppendLine("Response from WS")
End Sub
Public Sub bw_RunWorkerCompleted(ByVal Sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs)
_logDebug.AppendLine("Job completed")
Threads.ThreadFinished(Me)
End Sub
End Class
EDIT 1:
I think I've solved an issue with threads getting 'lost' - basically I have to SyncLock _threads in all Shared functions (SyncLock tw doesn't seem to do it's job). Still, this feels dirty and hacky at best. I feel like there is a better way to do things. I am currently looking into ThreadPool method, but I am a bit put off by its number of thread limits. I will potentially need to open thousands of threads.
What else I noticed is that the more concurrent threads I open, the longer it takes to get initial response from the WS. For example, if I work 1 record/thread at a time, it takes 1 second to get a response from WS. If I open 100 threads, it takes up to a minute to get a response. I suspect networking is buckling here, or maybe its a windows limitation?

How to make a loader in a separate thread?

I have a main form wich is expected to perfom some long operations. In parallel, I'm trying to display the percentage of the executed actions.
So I created a second form like this:
Private Delegate Sub DoubleFunction(ByVal D as Double)
Private Delegate Sub EmptyFunction()
Public Class LoaderClass
Inherits Form
'Some properties here
Public Sub DisplayPercentage(Value as Double)
If Me.InvokeRequired then
dim TempFunction as New DoubleFunction(addressof DisplayPercentage)
Me.Invoke(TempFunction, Value)
Else
Me.PercentageLabel.text = Value
End if
End sub
Public Sub CloseForm()
If Me.InvokeRequired Then
Dim CloseFunction As New EmptyFunction(AddressOf CloseForm)
Me.Invoke(CloseFunction)
Else
Me.Close()
End If
FormClosed = True
End Sub
End class
My main sub, the one which is expected to perform the long operations is in another form as follows:
Private Sub InitApplication
Dim Loader as new LoaderClass
Dim LoaderThread as new thread(Sub()
Loader.ShowDialog()
End sub)
LoaderThread.start()
Loader.DisplayPercentage(1/10)
LoadLocalConfiguration()
Loader.DisplayPercentage(2/10)
ConnectToDataBase()
Loader.DisplayPercentage(3/10)
LoadInterfaceObjects()
Loader.DisplayPercentage(4/10)
LoadClients()
...
Loader.CloseForm()
End sub
The code works almost 95% of the time but sometimes I'm getting a thread exception somewhere in the sub DisplayPercentage. I change absolutely nothing, I just hit the start button again and the debugger continues the execution without any problem.
The exception I get is: Cross-thread operation not valid: Control 'LoaderClass' accessed from a thread other than the thread it was created on event though I'm using : if InvokeRequired
Does anyone know what is wrong with that code please ?
Thank you.
This is a standard threading bug, called a "race condition". The fundamental problem with your code is that the InvokeRequired property can only be accurate after the native window for the dialog is created. The problem is that you don't wait for that. The thread you started needs time to create the dialog. It blows up when InvokeRequired still returns false but a fraction of a second later the window is created and Invoke() now objects loudly against being called on a worker thread.
This requires interlocking, you must use an AutoResetEvent. Call its Set() method in the Load event handler for the dialog. Call its WaitOne() method in InitApplication().
This is not the only problem with this code. Your dialog also doesn't have a Z-order relationship with the rest of the windows in your app. Non-zero odds that it will show behind another window.
And an especially nasty kind of problem caused by the SystemEvents class. Which needs to fire events on the UI thread. It doesn't know what thread is the UI thread, it guesses that the first one that subscribes an event is that UI thread. That turns out very poorly if that's your dialog when it uses, say, a ProgressBar. Which uses SystemEvents to know when to repaint itself. Your program will crash and burn long after the dialog is closed when one of the SystemEvents now is raised on the wrong thread.
Scared you enough? Don't do it. Only display UI on the UI thread, only execute slow non-UI code on worker threads.
Thank you for your proposal. How to do that please ? Where should I
add Invoke ?
Assuming you've opted to leave the "loading" code of the main form in the main UI thread (probably called from the Load() event), AND you've set LoaderClass() as the "Splash screen" in Project --> Properties...
Here is what LoaderClass() would look like:
Public Class LoaderClass
Private Delegate Sub DoubleFunction(ByVal D As Double)
Public Sub DisplayPercentage(Value As Double)
If Me.InvokeRequired Then
Dim TempFunction As New DoubleFunction(AddressOf DisplayPercentage)
Me.Invoke(TempFunction, Value)
Else
Me.PercentageLabel.text = Value
End If
End Sub
End Class
*This is the same as what you had but I moved the delegate into the class.
*Note that you do NOT need the CloseForm() method as the framework will automatically close your splash screen once the main form is completely loaded.
Now, over in the main form, you can grab the displayed instance of the splash screen with My.Application.SplashScreen and cast it back to LoaderClass(). Then simply call your DisplayPercentage() method at the appropriate times with appropriate values:
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
InitApplication()
End Sub
Private Sub InitApplication()
Dim Loader As LoaderClass = DirectCast(My.Application.SplashScreen, LoaderClass)
Loader.DisplayPercentage(1 / 10)
LoadLocalConfiguration()
Loader.DisplayPercentage(2 / 10)
ConnectToDataBase()
Loader.DisplayPercentage(3 / 10)
LoadInterfaceObjects()
Loader.DisplayPercentage(4 / 10)
LoadClients()
' Loader.CloseForm() <-- This is no longer needed..."Loader" will be closed automatically!
End Sub
Private Sub LoadLocalConfiguration()
System.Threading.Thread.Sleep(1000) ' simulated "work"
End Sub
Private Sub ConnectToDataBase()
System.Threading.Thread.Sleep(1000) ' simulated "work"
End Sub
Private Sub LoadInterfaceObjects()
System.Threading.Thread.Sleep(1000) ' simulated "work"
End Sub
Private Sub LoadClients()
System.Threading.Thread.Sleep(1000) ' simulated "work"
End Sub
End Class
If all goes well, your splash screen should automatically display, update with progress, then automatically close when your main form has finished loading and displayed itself.
Me.Invoke(TempFunction, Value)
Should be:
Me.Invoke(TempFunction, new Object(){Value})
because the overload with parameters takes an array of parameters.
Value is on the stack of the function in the current thread. You need to allocate memory on the GC heap and copy the value to that memory so that it is available to the other thread even after the local stack has been destroyed.

How easy is it to make a VB.net form button run logic in a separate thread

I'm not a VB coder but I'm tinkering with a little VB.net utility project which lets you set up a few parameters in a form and hit "go" - this then does a lot of logic which can run for several minutes.
This all happens in the go-button-handler which blocks the form. I wondered, is it easy in vb.net to make all this logic happen in a separate thread which can still update the form i.e. update a label to show which file is being processed? If it's complicated, it's not worth doing in my use-case!
Is it possible to just copy-paste my event code into a thread.Run or something like that, or even dynamically create a thread class around the code I have?
I have used the BackgroundWorker class (System.ComponentModel.BackgroundWorker) many times for things like this. It's very simple to use (compared to other multi-threading techniques available in .NET). Just drag it from the tool box onto your form, for example. If you set it's "WorkerReportsProgress" and "WorkerSupportsCancellation" properties to "True", you can even give feedback in your UI in the form of a progress bar, for example, and provide the ability for the user to click the cancel button.
Anyway, there's a lot more information about it than I can reasonably include here, so I would start by looking at this page:
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
BackgroundWorker is a good choice to start. Whatever you use be aware that performance can be affected if the background thread is processor intensive i.e. a long running tight loop. This might not be very obvious if you have a multi-core CPU.
Here is a simple example of Threading.Thread.
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Button3.Enabled = False
'for example pass a string and an integer to a thread as an array
Dim params() As Object = {"one", 1} 'parameters for thread. object picked because of mixed type
Dim t As New Threading.Thread(AddressOf someThread)
t.IsBackground = True
t.Start(params) 'start thread with params
End Sub
Public Sub someThread(params As Object)
'not on the UI
Dim theparams() As Object = DirectCast(params, Object()) 'convert object to what it really is, an array of objects
Dim param1 As String = DirectCast(theparams(0), String)
Dim param2 As Integer = DirectCast(theparams(1), Integer)
Debug.WriteLine(param1)
Debug.WriteLine(param2)
showOnUI(param1)
End Sub
Public Sub showOnUI(s As String)
If Me.InvokeRequired Then
'not running on UI
Me.Invoke(Sub() showOnUI(s)) 'run method on UI
Else
'running on UI
Label1.Text = s
Button3.Enabled = True
End If
End Sub

Threads in VB.NET

Well, I guess that I'm not the first who ask this, but what is the easiest way to use threads in VB.NET? I mean, I need to download some string from a remote server and then to show that string in the GUI, so I have to use some callback function to call it in the main thread. I found different approaches for this, but all seems very difficult compared to Python where (with GTK) I used something like:
gobject.idle_add(callback_function, parameters)
and "callback_function" was executed in the main thread. How I do that in VB.NET?
Here's a simple example. It may be a little more difficult than some other languages, but it's still not terribly complicated. The following code assumes that it is within a form class (if not, you'd need to use some control or form reference to call Invoke):
Private Sub beginDoWork()
Dim thread As New Thread(AddressOf DoWork)
thread.Start()
End Sub
Public Sub DoWork()
Dim result As String = getStringFromRemoteServer()
workCompleted(result)
End Sub
Private Delegate Sub workCompletedDelegate(result As String)
Private Sub workCompleted(result As String)
If InvokeRequired Then
Invoke(New workCompletedDelegate(AddressOf workCompleted(result)
Exit Sub
End If
Label1.Text = result
End Sub
This could be further simplified by just having DoWork always call Invoke to call workCompleted rather than have workCompleted check if the invoke is required, but the way I wrote it is a bit more encapsulated and efficient if you are ever going to do the work on the UI thread instead of a worker thread.