Async event handler - flycapture from PointGrey - vb.net

I am using Point Grey's FlyCapture API to drive some cameras.
In a public class, I implemented all the starting and initializing code ; in the following _cam refers to a ManagedGigECamera.
Because I have 16 cameras, I want the code to be as fast as possible, so I wanted to use tasks.
Here is the code I use:
_cam.StartCapture(AddressOf OnImageGrabbed)
.../...
Public Sub OnImageGrabbed(ByVal raw_image As ManagedImage)
Dim t As Task = Task.Run(Sub()
'save image to disk or whatever
End Sub)
t.Wait()
End Sub
The above gives -sort of- satisfaction. By viewing image timestamps, I can see that some images are saved seconds after they are grabbed, and even some images are skipped altogether...
I wanted to make sure each call to OnImageGrabbed would start a new task, and tried the following, but it crashes right away with 'object not set to an instance of an object' (can't really debug, the code is running on a remote machine)
_cam.StartCapture(AddressOf OnImageGrabbed)
.../...
Public Async Sub OnImageGrabbed(ByVal raw_image As ManagedImage)
Await Task.Run(Sub()
'save image to disk or whatever
End Sub)
End Sub
All in all, my questions are:
how can I run an event handler asynchronously ?
why, using the first code, do I get (what appears to be) random delays between each call
to OnImageGrabbed ? I mean the differences in time between image timestamps is never the same, and tend to increase on the long run (first few images are almost synchronized, but after 1 minute or so, each image is separated by more and more time). Memory leak ? GC ?
Thanks in advance for any hint !
EDIT:
In the end I changed the way the system works: I fire a software trigger on each camera using a timer, and each trigger is fired 'in parallel':
Parallel.ForEach(Of ListOfCameras)(SingleCamera,
Sub(aCamera, loopstate, num)
aCamera.FireTrigger()
End Sub)

Starting a task and then immediately blocking on it (via Wait) nets you nothing. You may as well just run the saving-image code directly.
The second example is actually asynchronous. You're probably getting an exception because the ManagedImage argument or one of its child objects is being disposed. Remember that the code raising the event has no idea that your code is asynchronous; it's up to you to copy out what you need from the event arguments if you're going to use it asynchronously.

Related

Waiting for an async function in vb.net

I'm trying out some async code to avoid locking up the UI while my program runs a time-consuming function (using Visual Studio 2022).
Here's what I've got so far - the program is running through pdf filename entries from a datagrid and performing the function on the filenames it finds:
Async Sub process_files()
For Each myrow In DGV_inputfiles.Rows
inputPDF = myrow.cells("col_filenamefull").value
outputPDF = myrow.cells("col_outname").value
Await Task.Run(Sub()
time_consuming_function(inputPDF, outputPDF)
End Sub)
Next
End Sub
At the moment, the program is not waiting for the 'time_consuming_function' to finish, so it's getting to the end of the sub before some of the output files are generated - so it appears to the user that it has finished when it's actually still working.
I believe the solution is something to do with returning a value from the function and waiting for it, but I can't quite see how it works - could anyone help please?
in time_consuming_function(...) you can send some infos to UI using invoke like this (assuming textbox1 exists in UI form):
sub time_consuming_function(...)
.... your stuff....
Me.BeginInvoke(Sub() textbox1.Text = "running...")
....
end sub
The effect of Await is that it returns control to the UI until the expression or call that is Awaited completes. It seems to be suited reasonably well to your workflow, you just need to make changes to process_files to be more user-friendly.
For example, you could have something in the UI update with the file that is currently being processed, and change it at the line before Task.Run.
e.g.
'(Inside the loop body)
CurrentOperation = $"Processing {inputPdf} into {outputPdf}..."
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(NameOf(CurrentOperation)))
Await Task.Run(...)
You could disable UI controls before the For loop and re-enable them when it finishes.
The benefit of Await is that these changes will be easy, and the logical flow of the routine will be easy to follow.
Be aware that any Await presents an option for re-entrant code as the user may interact with the UI (this is true even for cases where everything is running on one thread as with async internet or I/O operations).
If you haven't done so already, I would recommend to read everything Stephen Cleary has written about asynchronous operations in .NET.

How to limit the number of processes being spawned at a time?

I am working on a VB.NET Windows Forms application where the user is supposed to be able to determine how many processes the application is allowed to launch at a time.
My current method mostly works but I've noticed that occasionally the application goes over the set amount. I use two global variables for this, _ConcurrentRuns which is 0 at the start of the application, and _MaxConcurrentRuns which is set by the user.
Private _sync As new Object()
' This is called Synchronously
Private Function RunModel() As Boolean
If CancelExectuion Then Return CancelCleanup()
Do While True
SyncLock _sync
If _ConcurrentRuns < _MaxConcurrentRuns Then
Interlocked.Increment(_ConcurrentRuns)
Exit Do
End If
End SyncLock
Threading.Thread.Sleep(50)
Loop
'This is what will launch an individual process and close it when finished
ret = RunApplication(arg)
' The process has been closed so we decrement the concurrent runs
Interlocked.Decrement(_ConcurrentRuns)
Return ret
End Function
The goal is to let only one thread exit the while loop at a time, I'm not able to catch it in the debug mode however in the task manager it will occasionally go 1-3 processes over what it's supposed to use. This makes me assume that somehow multiple threads are getting inside the synclock somehow, but I have no clue how that could be happening.
I will be very grateful for any and all help that can be provided, thanks for taking the time to read my question.
So it appears that my solution works for this, I don't want to delete this question because it might be helpful to somebody else in the future.
Answer: Use better process monitoring software / set priority to high in task manager.

Invoke method for multi thread application?

I have a bug in my application which is the same as here which this person was running into the same problem. My application is multi threaded where the worker thread is updating the Waveformgraph on the UI. I believe that is where my problem is and why, periodically, and on occassion I get a big red X in at least one of my waveformgraph objects when running the application. From reading and research, I need to use an Invoke or BeginInvoke method? Can someone please explain better and provide a sample code that is relevant to my code? The samples that I've found so far still have me hazy on how I need to do this or what I need to do. Thank you for your help.
This code is on the swScopeOnOff click event, main thread.
thread2 = New System.Threading.Thread(AddressOf dataAcquiring)
thread2.Start()
This code is in dataAcquiring Sub
Public Sub dataAcquiring()
'While Scope switch is on, stream each Ai channel's data continuously to its respective WaveForm graph
Do While swScopeOnOff.Value = True
data = reader.ReadWaveform(readRate)
i = 0
For Each WaveformGraph In WFGS
WaveformGraph.PlotWaveformAppend(data(i)) 'This line is updating the UI's waveform graphs
i += 1
Next
i = 0
Loop
End Sub
Proper, thread-safe invocation is actually not as hard as one might think (not even for thread-safe events, but that's irrelevant for this question).
I would recommend you to use the normal Invoke method, such as Me.Invoke() (where Me is the current form, if not, use Form1 or whatever it's called instead). Using BeginInvoke() may be asynchronous but it stacks memory usage and can cause memory leaks if EndInvoke() is not called correctly.
If you target .NET 4.0 or higher you can simply do like this:
Me.Invoke(Sub() WaveformGraph.PlotWaveformAppend(data(i)))
However if you target .NET 3.5 or lower it requires a few more lines of code.
'Outside your Sub.
Delegate Sub WaveformAppendDelegate(ByRef WaveformGraph, ByRef data)
'Create a new sub.
Public Sub AppendData(ByRef WaveformGraph, ByRef data)
WaveformGraph.PlotWaveformAppend(data)
End Sub
'Inside your sub, when you're going to invoke.
Me.Invoke(New WaveformAppendDelegate(AddressOf AppendData), WaveformGraph, data(i))

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...

How to limit CPU usage in a while loop

How do you limit the CPU of a while loop?
In this case, the code which is inside the while loop:
Private Sub wait(ByVal time)
Dim sw As New Stopwatch
sw.Start()
Do While sw.ElapsedMilliseconds < time And StillOpen = True
Application.DoEvents()
Loop
sw.Stop()
End Sub
But now, here is the issue. This loop is allowing the while loop to run every second, once a second, and the wait sub is causing this delay, as it should.
How can I limit the CPU that this is taking up? For some reason, my task manager says it is taking 50 CPUs to run this simple task, yet it should probably take no more than 1 or 2. Though the manager says it is taking that much CPU, my computer speed is not being affected at all, which is odd considering it is a two-year-old laptop.
I don't want any users to freak out about it, but knowing how people are these days....
Anyway, the language is vb.net. Can someone please help me?
Thanks!
EDIT: To clarify, that code is not inside the while loop itself, but a call for the subroutine is, i.e. wait(1000)
Use a timer event !!! Nearly no cpu effort.
You could always perform some kind of sleep between iterations of the loop...
I'm not familiar with VB.NET but a duration of 100-200ms will probably be more than enough to drop the CPU usage.
Eg:
Do while (...)
Application.blah();
System.Threading.Thread.Sleep(150);
End
Edit After some research, I think the function you want is: System.Threading.Thread.Sleep()
Your code is executing Application.DoEvents() constantly in the while loop, for the time duration specified in your time parameter. This will consume one core of your CPU, which is why you're seeing 50% processor usage (you have a dual-core processor, correct?). This is an ugly way to wait. You could instead call Thread.Sleep(), passing it the number of milliseconds you'd like your thread to wait.
If you'd like your application to stay responsive, you might also spin off a timer, and block the UI from any action until the timer triggers. Something like (lightly tested):
// constructor or designer code
System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
timer.Tick += new EventHandler(timer_Tick);
void Wait(int interval)
{
timer.Interval = interval;
timer.Start();
BlockUIOperations(); // implement yourself
}
void timer_Tick(object sender, EventArgs e)
{
timer.Stop();
EnableUIOperations(); // implement yourself
}
Here's my attempt at a translation into VB:
'' Add a Timer object to the form named "Timer".
'' Hook its Tick event to Timer_Tick
Private Sub Wait(ByVal interval As Integer)
Timer.Interval = interval
Timer.Start()
BlockUIOperations() '' implement yourself
End Sub
Private Sub Timer_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer.Tick
Timer.Stop()
EnableUIOperations() '' implement yourself
End Sub
Well, the CPU is always running at 100% when it's running, so the only practical way to limit the CPU usage is to run bursts or loop and sleeping in between.
Laptop CPUs usually have some SpeedStep technology or equievalent that will slow down the CPU when it's not working hard, but it's not reasonable to assume that your application would have access to control that, at least not directly. You might be able to affect it indirectly by measuring the CPU usage and adjust the length of the work and sleep cycles to get the desired result.
If you don't mind blocking the current thread, you could use a WaitHandle.
Public Sub Wait(ByVal ms As Integer)
Using wh As New ManualResetEvent(False)
wh.WaitOne(ms)
End Using
End Sub
Sub Main()
Console.WriteLine("Hello World!")
Wait(5000)
Console.WriteLine("Good-Bye!")
End Sub
Of course, something more complex can be constructed depending on what you are trying to accomplish.
This is perfect as a VB.net sleep replacement. Now my console app is NOT reported as non responsive since I have no sleep commands!
Just add Imports System.Threading above your module and place this just above your sub main
Public Sub Wait(ByVal ms As Integer)
Using wh As New ManualResetEvent(False)
wh.WaitOne(ms)
End Using
End Sub
Then, in your sub main, use
wait(100)
to pause your app for 100 miliseconds.
Have fun
You should take note of if you are doing this in the main UI Thread or a thread you have spun off.
For Threads the easiest way is to just Thread.Sleep(x miliseconds)
On the main UI thread I tend to use a DoEvents function in vb.net and vb6 like this
Public Sub TimeKiller(byval secondstowait as integer)
dim tmptime as datetime = datetime.now
do while datetime.now < dateadd("s",secondstowait,tmptime)
Application.Doevents
end while
End Sub
On the question of CPU usage I look at it like this.... if you make just a hard loop that like
while true
end while
I would expect to see very high cpu usage over 50% because the UI thread is hard blocking on this.... in most cases the windows system will limit the cpu usage of any given program so that its threads dont block the entire system.
The DoEvents ensure that windows message pumps fire correct and respond to correct. It also ensures that the garbage collector fires on time.
Also if you have other threads spun up off of your UI.Thread your UI.Thread can respond to events fired from these other threads....
In such cases where your calling form controls from other threads and do form.InvokeRequired routines will be able to respond correctly.
Also The only time you should be hard looping on the MainUI thread is when it is in response to some user activity and you need to put waits in for the user to see progress of something....
If it is some kind of automated process that is always running... look to moving it to another thread.
Or if its something that runs periodically on a timer or a time that kicks off a thread.
Somebody please tell me if I am wrong on these assumptions....
Not sure about the Using wh As New ManualResetEvent(False) wh.WaitOne(ms) as I have never heard of that and have no idea what that does.