Gents,
Been a while since I did any VB work, and have started a new project that is a Windows Service that will update a database using multiple threads. I am testing this out with a dataset that consists of a "device name" and a "device IP", pinging the device, and attempting to update the device's "status".
I have it working in a single-thread type configuration, but would like to be able to ping and update different records across multiple threads (for obvious reasons).
The problem I'm having is with the logic mainly. When the service is started, I get all the devices from the database and fill a DataSet. From there, I am looping through the dataset's rows, pinging each device and updating the record. I know how to spawn multiple threads, but I do not know how to keep track of updating the correct record and also making sure that all records are checked in some kind of order. Like, if user specifies 5 threads, I need it to get record 1, thread finishes, gets record 6 (while other threads execute), or something like that.
What's the best approach here.
Note: this does no interface updating or anything like that. Only updates a database record's field value.
Re: Update database records using multiple threads
Do you really need to let the user specify the number of simultaneous operations? Generally speaking, it's better to simply use the ThreadPool and let the system handle scheduling.
Re: Update database records using multiple threads
Hey JMC, good to see you're still on the board!
I just finished getting a ThreadPool version working and seems to be working properly. Got a little stuck with passing a value to the procedure being executed by the Thread, but wrapping it in a separate small class did the trick. Still very new to the mutli-threading stuff, but am understanding the concept a little bit more with each example I come across.
Code:
objEventLog.WriteEntry("Scanning Started")
For Each row As DataRow In ds.Tables("Devices").Rows
rowIndex += 1
Dim ti As New TaskInfo(row(2), rowIndex)
ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf StartScan), ti)
If rowIndex > ds.Tables("Devices").Rows.Count - 1 Then
objEventLog.WriteEntry("Scan Completed at " & Date.Now)
Exit For
End If
Next
One thing I can't figure out is how to tell when my "scan" is completed (ie: all threads have completed, so that I can reset my variables to "loop" it). I don't see any ThreadPool features that do this.
Also, side question, does the "SyncLock" thing only come into play when a UI is involved?
Re: Update database records using multiple threads
Originally Posted by stateofidleness
One thing I can't figure out is how to tell when my "scan" is completed (ie: all threads have completed, so that I can reset my variables to "loop" it). I don't see any ThreadPool features that do this.
You might want to check out this discussion that involves the use of WaitHandles.
Also, side question, does the "SyncLock" thing only come into play when a UI is involved?
SyncLock has nothing specifically to do with a UI thread. You would use it any time that you have a critical section, i.e. a block of code that must be executed by no more than one thread at a time.
For instance, if you have code that increments a variable and then uses the value, it would be possible for two threads to interfere with each other without a SyncLock. The first thread might increment the variable and then be switched out and the second thread increment the variable and then be switched and then have the first thread use the value that has been incremented twice instead of once.
Re: Update database records using multiple threads
Wouldn't it be better to package the functionality within a class and give it progress and completed type events ? I find that usually its the best design for asynchronous tasks.
C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter
There's just no reason to use garbage like InputBox. - jmcilhinney
The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber
Re: Update database records using multiple threads
Originally Posted by Niya
Wouldn't it be better to package the functionality within a class and give it progress and completed type events ? I find that usually its the best design for asynchronous tasks.
If it was a complex app and/or the code was to be re-used then yes, I would absolutely suggest that. If it's just a simple app and the code will be used in one place then, while it would certainly not be wrong to do so, I wouldn't bother.
Re: Update database records using multiple threads
Originally Posted by Niya
Wouldn't it be better to package the functionality within a class and give it progress and completed type events ? I find that usually its the best design for asynchronous tasks.
I'm not sure what functionality specifically you're suggesting I package in its own class. Currently, I'm receiving a list of devices at Service start, then iterating through the dataset to ping each device (each ping using a thread in the ThreadPool).
Eventually, this ping will be changed to do more complex and time-consuming tasks for each device, but not sure what exactly should be put it its own class.
The pinging itself is represented and implemented in a Class that represents a single device. You can then call the ping on each device in a list created from data received from a database. This is the idea I'm getting at.
I've attached a complete implementation of a simulation based on what you described. Just click the Start Pinging button on the Form and see what happens. Each ping operation is performed on a separate thread pool thread. Observe how I handle both the completion of the ping operation of each device and the completion of all ping operations.
Last edited by Niya; May 29th, 2012 at 12:11 AM.
Reason: More Info.
C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter
There's just no reason to use garbage like InputBox. - jmcilhinney
The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber
Re: Update database records using multiple threads
Man, that looks pretty awesome, but it uses a lot of stuff I'm not familiar with (like BindingList). What's the "g_" convention?
I really don't know how this works
Is the example actually pinging things?
maybe i'm out of my league here lol
I like the idea of using a "Device" class as it's something I think I could use in other projects, but I'm not sure how to get the dataset and the class to "talk".
Re: Update database records using multiple threads
"g_" is a convention I personally use to represent private fields, sometimes referred to as module level variables.
Think of a BindingList(Of T) as a special List(Of T) object that is designed specifically for data binding. You can replace the BindingList with a List if you want since I didn't use it here for anything more than display purposes so it doesn't really need all the special functionality.
but I'm not sure how to get the dataset and the class to "talk".
You don't actually, you would use the data from the DataSet to instantiate Device objects.
Is the example actually pinging things?
No, its just a simulation to demonstrate multithreading.
C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter
There's just no reason to use garbage like InputBox. - jmcilhinney
The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber
C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter
There's just no reason to use garbage like InputBox. - jmcilhinney
The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber
Ability to have a function run on the calling thread when an event in the class is raised is exactly what I'm looking to do right now in a service app I'm writing.
Running your sample app made it all make perfect sense instantly!
*** Read the sticky in the DB forum about how to get your question answered quickly!! ***
Please remember to rate posts! Rate any post you find helpful - even in old threads! Use the link to the left - "Rate this Post".
C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter
There's just no reason to use garbage like InputBox. - jmcilhinney
The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber
Re: Update database records using multiple threads
Yup - I set break points all over the place and watched and saw how it all went down. Very nice.
Quick question - why a BINDINGLIST(Of) instead of a LIST(Of)???
I'm developing an app - a central service running on a network. All the UI's running on the network send "requests" to the service which is going to create a LIST(Of) my "processing class" and run some async method within that class.
When the async method complete the calling service needs to examine the results and send info back to the UI's.
As the UI's amend their request the "processing class" that I create for each UI will alter some properties on that class and then run some more async methods.
*** Read the sticky in the DB forum about how to get your question answered quickly!! ***
Please remember to rate posts! Rate any post you find helpful - even in old threads! Use the link to the left - "Rate this Post".
Re: Update database records using multiple threads
For this example a BindingList(Of T) wasn't necessary. A List(Of T) would have sufficed.
A BindingList(Of T) is exactly like a List(Of T) except that it has events that are used to detect changes in the items of the list and changes to the list itself. If you use a List(Of T) in a data-binding scenario, you would find that bound controls would not detect these changes and update their display. In my example, I don't alter anything in the list of Devices after binding so a BindingList wasn't really necessary.
A BindingList(Of T) also allows a bound DataGridView to add rows to the list should they desire such. A List(Of T) doesn't.
This project you're working on, are you passing .Net objects across the network ?
C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter
There's just no reason to use garbage like InputBox. - jmcilhinney
The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber
Re: Update database records using multiple threads
Yes - I am serializing and de-serializing a "transmission" class among a UI app (running multiple times on a network) and three background "service" apps - all using HttpListener methods.
I am really enjoying this type of coding - it's not paying yet but we have hopes!
*** Read the sticky in the DB forum about how to get your question answered quickly!! ***
Please remember to rate posts! Rate any post you find helpful - even in old threads! Use the link to the left - "Rate this Post".
C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter
There's just no reason to use garbage like InputBox. - jmcilhinney
The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber
Re: Update database records using multiple threads
Originally Posted by szlamany
Would that be the .Send and .Post methods of that class? Is that how it would raise event code to be run on a different thread?
Correct. Send is analogous to Invoke and Post is analogous to BeginInvoke. There are examples of using SynchronizationContext in a couple of my CodeBank threads: Accessing Controls From Worker Threads and also Asynchronous TCP.
Re: Update database records using multiple threads
Niya, I think what threw me off at first was the use of a UI in the example. I'm working with a Windows Service that will update a database. The front-end will be a web-based UI (PHP). I may add a Windows Form front-end at some point, but do the same "rules" apply to a non-Form type service? (ie: can I still raise events and know when they complete, etc?)
I'm not really familiar with this syntax format:
AddHandler dev.PingProgressChanged, AddressOf PingProgressChangedEventHandler
Re: Update database records using multiple threads
Originally Posted by jmcilhinney
You appear to be creating a Control of your own. You should rather be using the SynchronizationContext class.
I donno. It seems to do much more than I really need and it looks a little intimidating considering all I want to do is invoke a delegate on the UI thread. Using a Control object is elegantly simpler it seems but of course I've never used the SynchronizationContext so I don't really know. I'll experiment with this at a later time and see what its about.
EDIT:
Oh you posted while I was typing this. Can that SynchronizationContext object be used with my approach without significant modifications ?
C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter
There's just no reason to use garbage like InputBox. - jmcilhinney
The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber
Re: Update database records using multiple threads
Originally Posted by stateofidleness
Niya, I think what threw me off at first was the use of a UI in the example. I'm working with a Windows Service that will update a database. The front-end will be a web-based UI (PHP). I may add a Windows Form front-end at some point, but do the same "rules" apply to a non-Form type service? (ie: can I still raise events and know when they complete, etc?)
I'm not really familiar with this syntax format:
AddHandler dev.PingProgressChanged, AddressOf PingProgressChangedEventHandler
What's that saying in "pseudo" code terms?
Ya you can use the pattern without a UI if you want. It won't cause any problems.
Here is the documentation for AddHandler. It attaches event handlers to object events.
C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter
There's just no reason to use garbage like InputBox. - jmcilhinney
The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber
Re: Update database records using multiple threads
Originally Posted by Niya
Oh you posted while I was typing this. Can that SynchronizationContext object be used with my approach without significant modifications ?
There would be some change but not much. One advantage with a SynchronizationContext is that it will work in WPF without change and also in non-GUI scenarios, where creating a control would be quite wrong.
C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter
There's just no reason to use garbage like InputBox. - jmcilhinney
The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber
Protected Overridable Sub OnSomeEvent(e As EventArgs)
RaiseEvent SomeEvent(Me, e)
End Sub
End Class
EDIT: Note that the action Lambda used as an argument to Send requires .NET 4.0 or later but you can easily enough do the equivalent with a named method in an earlier version.
Last edited by jmcilhinney; May 29th, 2012 at 08:56 PM.
C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter
There's just no reason to use garbage like InputBox. - jmcilhinney
The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber
While it is quite similar to what I had before, I have to say that I find Invoke to be a superior function for the simple reason it takes a paramarray for the arguments of the delegate and it can use a delegate of any signature. Send is far too limited. Its a good thing that OnEvent type methods only take one parameter of an EventArgs class else it could have gotten real clumsy. The Send method should have had the same signature as Invoke, donno why MS didn't do it so.
C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter
There's just no reason to use garbage like InputBox. - jmcilhinney
The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber
Re: Update database records using multiple threads
Originally Posted by Niya
While it is quite similar to what I had before, I have to say that I find Invoke to be a superior function for the simple reason it takes a paramarray for the arguments of the delegate and it can use a delegate of any signature. Send is far too limited. Its a good thing that OnEvent type methods only take one parameter of an EventArgs class else it could have gotten real clumsy. The Send method should have had the same signature as Invoke, donno why MS didn't do it so.
I actual fact, the code you posted won't work with Option Strict On, which it should always be, because the SendOrPostCallback has a single Object parameter, just like most methods related to multi-threading do. That's why it's preferable to use a Lambda. My example could look like this without the Lambda:
vb.net Code:
Public Class SomeClass
'This will get the context for the current thread so this instance must be created on the UI thread.
Private context As SynchronizationContext = SynchronizationContext.Current
Public Event SomeEvent As EventHandler
Private Sub RaiseSomeEvent()
If context Is Nothing Then
'This is not a GUI app so raise the event on the current thread.
RaiseSomeEvent(EventArgs.Empty)
Else
'This is a GUI app so raise the event on the UI thread.
Protected Overridable Sub OnSomeEvent(e As EventArgs)
RaiseEvent SomeEvent(Me, e)
End Sub
End Class
All you need is the one extra method with an Object parameter that casts it as the appropriate type and then calls the final method. Three extra lines of code.
Does that go into OnSomeEvent with a reference to the calling function - kind of like the THIS in Javascript and how closure works to make local declarations live into the called function?
*** Read the sticky in the DB forum about how to get your question answered quickly!! ***
Please remember to rate posts! Rate any post you find helpful - even in old threads! Use the link to the left - "Rate this Post".
C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter
There's just no reason to use garbage like InputBox. - jmcilhinney
The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber
Re: Update database records using multiple threads
Originally Posted by jmcilhinney
I actual fact, the code you posted won't work with Option Strict On, which it should always be, because the SendOrPostCallback has a single Object parameter, just like most methods related to multi-threading do. That's why it's preferable to use a Lambda. My example could look like this without the Lambda:
vb.net Code:
Public Class SomeClass
'This will get the context for the current thread so this instance must be created on the UI thread.
Private context As SynchronizationContext = SynchronizationContext.Current
Public Event SomeEvent As EventHandler
Private Sub RaiseSomeEvent()
If context Is Nothing Then
'This is not a GUI app so raise the event on the current thread.
RaiseSomeEvent(EventArgs.Empty)
Else
'This is a GUI app so raise the event on the UI thread.
Protected Overridable Sub OnSomeEvent(e As EventArgs)
RaiseEvent SomeEvent(Me, e)
End Sub
End Class
All you need is the one extra method with an Object parameter that casts it as the appropriate type and then calls the final method. Three extra lines of code.
Oh, I almost forgot to post this up...wrote it and fell asleep after:-
vbnet Code:
Option Strict On
Imports System.Threading
Imports System.Runtime.CompilerServices
'Imagine this class to be a class used
'to represent each device retrieved from the database
Public Class Device
Private _DeviceID As Integer
Private _DeviceName As String
Private _SomeDeviceProperty As Integer
Private g_bPingingAsync As Boolean = False
Private context As SynchronizationContext = SynchronizationContext.Current
Public Event PingProgressChanged As EventHandler(Of PingProgressChangedEventArgs)
Protected Overridable Sub OnPingProgressChanged(ByVal e As PingProgressChangedEventArgs)
RaiseEvent PingProgressChanged(Me, e)
End Sub
Public Event PingProgressCompleted As EventHandler
Protected Overridable Sub OnPingProgressCompleted()
RaiseEvent PingProgressCompleted(Me, New EventArgs)
End Sub
Sub New()
End Sub
Sub New(ByVal devName As String, ByVal devID As Integer, ByVal someProp As Integer)
MyClass.New()
Me.DeviceID = devID
Me.DeviceName = devName
Me.SomeDeviceProperty = someProp
End Sub
Public Property DeviceID() As Integer
Get
Return _DeviceID
End Get
Set(ByVal value As Integer)
_DeviceID = value
End Set
End Property
Public Property DeviceName() As String
Get
Return _DeviceName
End Get
Set(ByVal value As String)
_DeviceName = value
End Set
End Property
Public Property SomeDeviceProperty() As Integer
Get
Return _SomeDeviceProperty
End Get
Set(ByVal value As Integer)
_SomeDeviceProperty = value
End Set
End Property
Public Sub PingDevice()
InternalPingDevice()
End Sub
Public Sub PingDeviceAsync()
If g_bPingingAsync Then
Throw New Exception("Alreadying pinging asyncrounously")
Public Sub Invoke(ByVal sc As SynchronizationContext, ByVal del As [Delegate], ByVal ParamArray args() As Object)
sc.Send(New SendOrPostCallback(AddressOf SendOrPost), New SendOrPostState With {.Args = args, .DelegateToInvoke = del})
End Sub
Private Sub SendOrPost(ByVal state As Object)
Dim st As SendOrPostState = DirectCast(state, SendOrPostState)
st.DelegateToInvoke.DynamicInvoke(st.Args)
End Sub
Private Class SendOrPostState
Public Args() As Object
Public DelegateToInvoke As [Delegate]
End Class
End Module
I'm using VS 2008 so I can't use Sub to create a lambda expression and I don't really like the idea of having to declare an extra OnEvent type procedure just to support Send so I created an Invoke extension method for the SynchronizationContext class which works like the Invoke method on the Control class. This way works with Option Strict.
C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter
There's just no reason to use garbage like InputBox. - jmcilhinney
The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber
Re: Update database records using multiple threads
Originally Posted by Niya
I created an Invoke extension method for the SynchronizationContext class which works like the Invoke method on the Control class. This way works with Option Strict.
Does that go into OnSomeEvent with a reference to the calling function - kind of like the THIS in Javascript and how closure works to make local declarations live into the called function?
*** Read the sticky in the DB forum about how to get your question answered quickly!! ***
Please remember to rate posts! Rate any post you find helpful - even in old threads! Use the link to the left - "Rate this Post".
C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter
There's just no reason to use garbage like InputBox. - jmcilhinney
The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber