Thread safe read from listbox object possible? - vb.net

I have stored objects in a listbox. I want to READ (Not write or edit) these objects from different threads. Is this threadsafe? And if not, how would I make it threadsafe?
I tried writing a threadsafe function, but this obviously does not return a value on all exits.
Private Delegate Function GetDetailsDelegate(ByVal index As Integer)
Private Function GetDetails(index As Integer) As MySQLDetails
If InvokeRequired Then
Invoke(New GetDetailsDelegate(AddressOf GetDetails), {index})
Else
Return LstMySQL.Items.Item(index)
End If
End Function

Related

Issue with updating the form using Async/Wait

In my application, I call a process to update software - which is stored within its own class. Even thou I have am Async/Wait and debug.print is returning a message within frmUpdate.ReportProgress() for some reason the progress bar in the update form is not updating...
Class
Namespace software
Public Class updater
Public Async Function UpdateSoftware(ByVal url As String, ByVal downloadFolder As String) As Tasks.Task(Of Boolean)
Dim progressIndicator = New Progress(Of Integer)(AddressOf ReportProgress)
Await SetProgressBar(progressIndicator, 100)
Return True
End Function
Private Async Function SetProgressBar(ByVal myProgress As IProgress(Of Integer), counter As Integer) As Tasks.Task
myProgress.Report(counter)
End Function
Private Function ReportProgress(ByVal count As Integer)
frmUpdate.ReportProgress(count)
End Function
End Class
End Namespace
Code for the Form Below
Public Class frmUpdate
Private Async Sub btnUpdate_Click(sender As System.Object, e As System.EventArgs) Handles btnUpdate.Click
updater.UpdateSoftware(url, downloadFolder)
End Function
Public Function ReportProgress(ByVal myInt As Integer)
ProgressBar1.Value = myInt
Debug.Print(ProgressBar1.Value)
End Function
End Class
Async and Await do not inherently make things multi-threaded. There's good reason for that. Not all asynchronous tasks are multi-threaded. For instance, if you want to have some task wait until the user clicks a button, you don't need that task to be on it's own thread. That would be very wasteful to have a separate thread just sitting there looping while it waits for the button to be clicked. In a situation like that, you'd just have the click event of the button signal the task to continue/complete. Then, the task can simply block execution until that signal is received. Since it's the case that making all asynchronous tasks multi-threaded is wasteful, starting a task in a new thread is left as a separate optional action.
Simply adding the Async keyword to your method signature does nothing to make your method run on a separate thread. In fact, it technically doesn't even make your method asynchronous. It will only be asynchronous if you call Await inside the method somewhere. So, there is no appreciable difference between your method:
Private Async Function SetProgressBar(ByVal myProgress As IProgress(Of Integer), counter As Integer) As Tasks.Task
myProgress.Report(counter)
End Function
And this:
Private Sub SetProgressBar(ByVal myProgress As IProgress(Of Integer), counter As Integer)
myProgress.Report(counter)
End Sub
Both methods execute immediately when they are called and both block execution in whatever method called them until they are complete. If myProgress, whatever that is, provides an asynchronous version of the Report method (i.e. an equivalent method that returns a Task), then you want to call that and await on it:
Private Async Function SetProgressBar(ByVal myProgress As IProgress(Of Integer), counter As Integer) As Tasks.Task
Await myProgress.ReportAsync(counter)
End Function
If no such asynchronous alternative exists, then you can force it to be asynchronous by starting it in its own thread:
Private Async Function SetProgressBar(ByVal myProgress As IProgress(Of Integer), counter As Integer) As Tasks.Task
Await Task.Run(Sub() myProgress.ReportAsync(counter))
End Function
However, in this case, I'm quite certain, given the name of the method, that it doesn't really need to be asynchronous at all. The real issue is that whatever the long-running work you're doing in UpdateSoftware is, it's not being done with Await, so it's what's blocking when it shouldn't be. But, since you didn't show that code, it's hard to give a good example. Consider something like this:
Public Class updater
Public Async Function UpdateSoftware(ByVal url As String, ByVal downloadFolder As String) As Tasks.Task(Of Boolean)
Await Task.Run(AddressOf LongRunningWork)
Return True
End Function
Private Sub LongRunningWork()
' Do something that takes a while
For i As Integer = 1 to 100
ReportProgress(i)
Thread.Sleep(100)
Next
End Sub
Private Sub ReportProgress(count As Integer)
frmUpdate.BeginInvoke(Sub() frmUpdate.ReportProgress(count))
End Function
End Class
Note that in the ReportProgress method, I have it calling BeginInvoke (though Invoke would work too) on the form to get it to execute that form's method on the UI thread rather than on the task's thread. That's always important to do. Anytime you are updating the UI, you always need to invoke back the the UI thread to do the updating, otherwise you'll get cross-thread exceptions.
Also, you aren't using Await in the event handler either, which you ought to do. Technically, it works either way, but if you start adding exception handling, you'll find out quickly it makes a big difference:
Private Async Sub btnUpdate_Click(sender As System.Object, e As System.EventArgs) Handles btnUpdate.Click
Await updater.UpdateSoftware(url, downloadFolder)
End Function
For more information about Async/Await (Microsoft calls it the Task-based Asynchronous Pattern, or TAP for short), see the documentation. TAP is a really powerful tool. It makes asynchronous code very easy to read and understand. But, despite appearing simple on the surface, it still requires a good understanding of the underlying concepts to use it properly. If you are feeling uncertain about it, you may want to try using the BackgroundWorker component, as I suggested in my answer to your previous question, since it's a little less magical and may be easier for you to see what's happening where and why.

VB form cross thread method call with parameters

I am writing a VB form application that redirects the standard output stream of a process and uses it in a UI.
I am having trouble calling methods with parameters that update controls on the form from the OutputHandler sub.
I can call a method without parameters like so
Me.Invoke(New MyDelSub(AddressOf ServerStarted))
Which works fine.
And a bit of googling told me that to call a method with parameters I should do this:
Dim del As JoinDelegate = AddressOf PlayerJoins
del.Invoke(username)
With this delegate and method pair:
Private Delegate Sub JoinDelegate(ByVal username As String)
Private Sub PlayerJoins(ByVal username As String)
PlayersBox.Items.Add(username)
'Do other stuff
End Sub
But this produces an IllegalOperationException the first time the method tries to access a control.
1) Supposing you have a method like this:
Public Sub DoSomething(value1 As String, value2 As String)
MessageBox.Show(String.Format("{0} {1}", value1, value2))
End Sub
You can call it using invoke this way:
Me.Invoke(Sub() DoSomething("Hello", "World!"))
2) If you want to make thread safe call to a control you can write the method this way:
Public Sub AddItemToListBox1(item As String)
If (ListBox1.InvokeRequired) Then
ListBox1.Invoke(Sub() AddItemToListBox1(item))
Else
ListBox1.Items.Add(item)
End If
End Sub
Then it's enough to call it in a the UI thread or in another thread the same way simply:
AddItemToListBox1("some item")
The call would be thread safe.
E.g.
Private Sub SetControlText(control As Control, text As String)
If control.InvokeRequired Then
control.Invoke(New Func(Of Control, String)(AddressOf SetControlText), control, text)
Else
control.Text = text
End If
End Sub
Call that method from any thread.

How to lock primitive static fields in VB.NET

I know you can obtain a lock instance members like so:
SyncLock [Object]
[Object].mutate()
End SyncLock
but how do you lock static fields?
(e.g. to make [Object's Class].[static field] = [new value] thread-safe)
I can't find anything online for VB.
The field is a primitive type.
As long as the object is a reference type, you can simply lock that object:
SyncLock [Class].[Object]
' … edit object
End SyncLock
Depending on your situation this may be the correct thing to do. However, be aware that this should ideally only be done on private objects, inside the class. Otherwise you have no guarantee that every client is going to lock the object correctly.
Whether your field is a primitive type isn’t important here, except that most primitive types are value types, and you cannot lock those. So, assuming that your type is a value type, you have to resort to a separate lock object as discussed in the comments:
Private Shared ReadOnly LockObj As New Object()
But, to clarify, locking on LockObj does not magically lock the rest of the class. It just provides a protocol for locking, and as long as every thread accessing the fields respects this protocol, you’re safe. Here’s an example:
Class Foo
Private Shared ReadOnly LockObj As New Object()
Private Shared MyValue As Integer = 1
Public Shared Sub UpdateValue()
SyncLock LockObj
MyValue += 1
End SyncLock
End Sub
Public Shared Function ReadValue() As Integer
SyncLock LockObj
Return MyValue
End SyncLock
End Function
End Class
As long as every thread uses only UpdateValue and ReadValue to access MyValue, you’re safe from race conditions.

VB.NET Dynamic CustomTypeDescriptor

I'm playing around with an idea(never played with TypeDescriptors before), and managed to get it to work nicely. But I'm concerned about some "best practice" decisions I made during my little experiment.
I use a CustomTypeDescriptor, that receives an event from its PropertyDescriptors indicating that the values are changing or being queried.
The TypeDescriptorProvider generates a completely new instance of CustomTypeDescriptor every time GetTypeDescriptor is called, it then binds the events on the CustomTypeDescriptor to the instance object.
I'm unsure whether or not generating a new CustomTypeDescriptor each time GetTypeDescriptor is called would be a good idea(even though I had to, to get this to work). I'm also unsure if there are any consequences with binding events directly from the CustomTypeDescriptor to the instance object, especially if the CustomTypeDescriptor is dynamic.
What do you guys think? Sample code of my Provider below:
Class EntityTypeDescriptionProvider
Inherits TypeDescriptionProvider
Public Sub New(ByVal parent As TypeDescriptionProvider)
MyBase.New(parent)
End Sub
Protected Sub New()
End Sub
Public Overrides Function GetTypeDescriptor(ByVal objectType As Type, ByVal instance As Object) As ICustomTypeDescriptor
Dim _CustomTypeDescriptor As EntityTypeDescriptor
'Grabbin the base descriptor.
Dim Descriptor As ICustomTypeDescriptor = MyBase.GetTypeDescriptor(objectType, Nothing)
'If for...whatever reason the instance is empty, return the default descriptor for the type.
If instance Is Nothing Then
Return Descriptor
End If
'If the instance doesnt implement the interface I use for Descriptor customization
'(which should never happen) return the default descriptor for the type.
If Not Functions.IsImplemented(instance.GetType, GetType(ICustomTypeDescriptorEntity)) Then
Return Descriptor
End If
'
'
'some lengthy "customization" based on the state of the instance.
'
'
AddHandler _CustomTypeDescriptor.GetValue, AddressOf CType(instance, ICustomTypeDescriptorEntity).GetValue
AddHandler _CustomTypeDescriptor.SetValue, AddressOf CType(instance, ICustomTypeDescriptorEntity).SetValue
Return _CustomTypeDescriptor
End Function
End Class
I've been using this for a while and it hasnt blown up at all.
Feedback still welcome, I'm answering this to lay it to rest.

Static members in VB.NET

I used to write this:
Private Sub Example()
Static CachedPeople As List(Of MyApp.Person)
If CachedPeople Is Nothing Then
CachedPeople = New List(Of MyApp.Person)
End If
...rest of code...
End Sub
But then wondered if I could reduce this to:
Private Sub Example()
Static CachedPeople As New List(Of MyApp.Person)
...rest of code...
End Sub
The question is, will the "New" bit only be executed once when the function is first executed but in the next call, it will already exist.
Cheers, Rob.
It'll be executed only once and on next function call, it'll reference the same object, as you mentioned. Your first snippet is not thread-safe, by the way. If two threads call your function at the same time, they might end up running the constructor twice, which is not what you want. Using the second snippet relieves you from manually locking and ensuring thread safety, as the compiler generates the appropriate code for you.
Note that if you had declared it as
Static x As List(Of String)
x = New List(Of String)
It would have been recreated each time.