I have a custom-written background task manager that takes an Action as parameter, queues it, waits until an execution thread is free and then invokes it.
Now I wanted to do the following:
Dim lValue As Integer = 0I
For tIndex As Integer = 0 To 10
BackgroundTaskManager.QueueTask(New Action(Sub() DoSomething(lValue)))
lValue += 1
Next
This obviously leads to some strange results, because the lambda expression is invoked at an unpredictable moment (whenever a thread is available), so the DoSomething-method does not have access to the actual value that I want it to have.
However, putting these parameters into to the sub - which would require an Action(Of T, ...) - is not really an option, because my background task manager only takes Action without parameters and I want it to stay that way (the background task manager should not have any knowledge about what's actually being called).
What I'm looking for is a way to store a function pointer with parameter values already evaluated, ready to be invoked by a simple [FunctionWithParametersEvaluated].[Invoke].
Any idea how to achieve this?
Related
I am using a TCP client/server to receive information from a micro controller and using that information inside a Win Forms App, VB.NET. My issue is trying to check a label against a datatable in an IF statement. I have tried different forms of invoke but none of them seem to work.
VB Code
If lblStep.Invoke(New MethodInvoker(Sub() lblStep.Text = lblStep.Text), Nothing) = dt.Rows(0).Item("Step") Then
'Do something
end if
The error I am receiving is a multi thread error.
If you expect to get a value returned then you need to invoke a method that returns a value, which means a function. You could do this:
If CStr(lblStep.Invoke(Function() lblStep.Text)) = dt.Rows(0).Field(Of String)("Step") Then
or this:
If CBool(lblStep.Invoke(Function() lblStep.Text = dt.Rows(0).Field(Of String)("Step"))) Then
The first one gets the String from the UI thread and performs the comparison on the background thread while the second performs the comparison on the UI thread.
Note that I also make sure that everything is the correct data type, which is required if you have set Option Strict On, which you absolutely should have done.
How can I code a method in VB.Net 2012 that waits for a variable number of asynchronous calls to complete, and only when all calls finish will then return a result?
I'm writing an app that retrieves a value from various web pages, and then returns the sum of those values. The number of values to retrieve will be determined by the user at runtime. As web retrieval is asynchronous by nature, I'm trying to make the app more efficient by coding it as such. I've just read about the keywords Async and Await, which seem perfect for the job. I also found this example of how to do it in C#: Run two async tasks in parallel and collect results in .NET 4.5.
But there are two issues with this example: 1) At first glance, I don't know how to make the same thing happen in VB.Net, and 2) I don't know how it could be redesigned to handle a variable number of called tasks.
Here's a pseudo-translation from the example, of what I hope to achieve:
Function GetSumOfValues(n as Integer)
For i = 1 To n
GetValueAsync<i>.Start()
Next i
Dim result = Await Task.WhenAll(GetValueAsync<?*>)
Return result.Sum()
End Function
Note the question mark, as I'm not sure if it's possible to give WhenAll a "wildcarded" group of tasks. Perhaps with an object collection?
You can use this example of using tasks with Task.WaitAll
Now, to collect data asynchronously, you can use a static method with sync lock. Or one of the synchronized collections
I have the following code which is being called from a Web Api. As you can see I want to return as soon as I can and shift the work onto the threadpool. (The client polls to see when the job is complete. But the polling is nothing to do with this. The purpose of these routines is simply to extract data and write a file away in background whilst maintaining the progress of the job in a table. The client will interrogates this to determine whether the file is ready so I'm not trying to push progress messages to the client.)
Public Function Extract(filepath as string, ...) as ExtractResult
dim source = ExtractInternal(filepath, ...)
' works first time it is called only!
using source.SubscribeOn(ThreadPoolScheduler.Instance)
.SubScribe()
end using
' works every time it is called ...
dim subscription = source.SubscribeOn(ThreadPoolScheduler.Instance)
.SubScribe()
Return New ExtractResult()
End Function
Public Function ExtractInternal(filepath as string, ...) as IObservable(of Unit)
return Observable.Create(of Unit)
Function()
....
uses filepath here
Return Disposable.Empty
End Function
End Function
As you can see in my comments, if I use auto-disposing of Using ..., I am finding that the observable gets called on the first occasion but not thereafter. Whereas if I assign the subscription to a local var it works every time the web call invokes the routine but I'm concerned that I'm actually leaving stuff hanging around.
Could someone explain why the observable doesn't get re-instantiated on subsequent calls and perhaps explain how I can get it to work every time and tidy up afterwards properly.
EDIT:
So I ended up using Observable.Defer which seems tom give me what I am after ...
Public Function Extract(filepath as string, ...) As ExtractResult
Observable.Defer(Function() ExtractInternal(filepath, ...) _
.SubscribeOn(NewThreadScheduler.Default) _
.Subscribe()
Return New ExtractResult()
End Function
I'm wondering if this is perhaps the correct way to do it to give me proper disposal whilst also using the current parameter values.
Could anyone confirm or correct?
EDIT 2
That was wrong! In fact if I rewrite it as
Public Function Extract(filepath as string, ...) As ExtractResult
Using Observable.Defer(Function() ExtractInternal(filepath, ...)
.SubscribeOn(NewThreadScheduler.Default) _
.Subscribe()
End Using
Return New ExtractResult()
End Function
I get the same behaviour as I originally was getting when I wrote the post.
One thing (amongst many) I don't understand is why if the observable is local var, when a second call is made to the Extract method, another observable is not created and subscribed to? It seems to go against scoping logic if I am actually referencing the same observable under the hood? I've obviously misunderstood.
Many thx
S
Yes, when you dispose the subscription, it stops receiving notifications.
You should keep it in an instance field and have the class implement disposable. Consumers of this class can then dispose it at their convenience.
In your Dispose implementation, you call subscription.Dispose().
Bear with me here, ok!!
We use SMO a lot to do all kinds of things, including to check for the presence of particular stored procedures in the database. So we have a method in our data access class called HasProc, which returns a boolean. It's in a part of the application that hasn't been changed for over a year, possibly two years.
Lately, it's been taking ages (10s) to return a value, and I've been trying to pin down why.
It turns out that even defining the variable that will hold the SMO Server (not instantiating it, just defining it) causes a 10s delay in the code arriving into the function.
Here's the relevant bit of the code, which just returns True now, for clarity:
Public Function HasProc(ByVal storedProcName As String) As Boolean
Dim s As Microsoft.SqlServer.Management.Smo.Server
Return True
End Function
In Visual Studio 12, stepping through the code using F11, the 10 second delay happens before the code highlight arrives at Public Function etc...
If I comment out the Dim statement, it all runs instantly.
Even more weirdly, if I disable my ethernet adaptor, the delay does not occur.
This is reproducible across three computers. I'm using VS2012, and SMO v11, to which we recently upgraded in order to support SQL Server 2012.
The other thing is that the delay happens even if the Return True statement is before, rather than after the Dim statement.
Any ideas?
This would happen if the static initializer for that class performs network IO (which is generally a bad idea).
If you pause the debugger during the delay, you can find out exactly what it's doing.
I am communicating with a USB-HID device. It will successfully complete hundreds of send-receive requests but occasionally get a Null Exception error.
Is this a threading issue?
FormMain.vb:
myHidDevice.transmitPacket(Packet)
myHidDevice.resetEvent.WaitOne(6)
If myHidDevice.rxDataReady = True then
' Life is good
MyHidDevicePort.vb
Public Sub DataReceivedHandler(ByVal sender as Object, dataReceived as DataReceivedEventArgs)
if dataReceived.data Is Nothing Then
Exit Sub
Else
Dim rDataPacket As List(Of Byte) = dataReceived.data.ToList()
For Each element in rDataPacket
rxData.dataPacket(i) = element
rxDataReady = True
resetEvent.Set()
MySensorClass.vb
Public Overrides Function processPacket(ByRef rxStruct as rStruct, ByVal txPacket()) as Boolean
....
Select Case rxStruct.dataPacket(4)
Case MOD_DISPLAY_SET_BRIGHTNESS
rxData(0) = rxStruct.dataPacket(5)
...
at the rxData.dataPacket(i) = element I will get a NullReference error every now and then. I could enclose it in a try/catch statement, but I'd like to fix the root problem if possible.
This device is communicating to microcontrollers, and it is possible that they won't always give a value... but my feeling is this is some sort of UI threading issue. When debugging, even though there is a null exception, many times there actually does seem to be data in dataReceived.data.ToList(). Is there an easy way to place the whole data processing routine on a thread separate from the UI?
Edit: Changed code to match answer and give more info on where it is used. Still get NullReferenceExceptions after about 1,000 completed send/receive requests to the HID device.
The dialog in the comments is too restricting, so I'll try this as an answer. Unfortunately, there still isn't enough code to give a full answer.
Some sample unknowns:
What is rxData (a custome class, part of the SDK, a struct)?
Where does i come from in the code sample rxData.dataPacket(i) = element, I don't see it decalred or incremented.
Why is the form waiting on myHidDevice.resetEvent.WaitOne(6) and what does it do once it thinks there is sucess?
How/when does processPacket get called?
What I can recommend in general is that access to shared state be wrapped in a SyncLock. And in your case, that includes both rxData and rxDataReady.
In your threaded event callback you need this:
SyncLock(syncRoot)
For Each element in rDataPacket
rxData.dataPacket(i) = element
next
rxDataReady = True
resetEvent.Set()
End SyncLock
And in your Main form where you are waiting for a response you need to wrap access to the ready flag as well:
SyncLock(myHidDevice.syncRoot)
If myHidDevice.rxDataReady = True then
' do something that consumes the data read in the thread
End If
End SyncLock
You have to watch for how long you hold the lock in the read and the write because you cannot be doing both at the same time.
Over all, I wouldn't be surprised if your code code be refactored a bit to make the thread issues easier to deal with. A blocking queue / colletion as you suggested might be a good idea. Just not enough is known of you design/code to give any more concrete advice.
If you know that there is a possibility that your object may be null, placing it inside of a try-catch block would be the incorrect way of handling this situation, as that would be considered coding by exception. Instead, do a null check on your object prior to setting it. e.g.
If Not dataReceived.data Is Nothing Then
Dim rDataPacket As List(Of Byte) = dataReceived.data.ToList()
End If
If your problem lyes with the individual array elements being null, you should also check them to ensure they exist before setting/accessing them.