Threading.Task.Run Submits Twice But Only On Some Calls - process

Friends: this problem has me flummoxed.
This code submits the same job twice UNLESS I log it!
' optionally log to a textfile before call
If bLog Then LogProcess("Before run")
' this only processes once regardless of
ExecuteSQL($"UPDATE [Process] SET [timesRepeated] += 1, [dateStarted] = '{Now}' WHERE [id] = {iID}")
Dim oBackground As New Background
' this thing spawns two of the some - but not all - jobs!
Dim t1 As Task = Task.Run(Sub() CallByName(oBackground, sProcessKey, CallType.Method, iID))
' optionally log to a textfile following call
If bLog Then LogProcess("After run")
This happens as part of a background processing routine that is called by an internal timer.
I've verified that the timer is only spawned once and only fires once per "heatbeat".
If this routine is called with bLog = True, I get single executions. If bLog = False, SOME processes (sProcessKey) get called twice at the exact same time. I have logging in these routines as well.
Any thoughts?
Thanks in advance.

I hope this helps someone else. It's not a solution, only a work-around:
Since delaying processing by just a bit (via logging in this case) worked, I added a snooze factor, thusly:
If bLog Then
LogProcess("After run")
Else
Thread.Sleep(1000)
End If
This solves the issue, but obviously, delays processing for 1 second.

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.

Blocking main thread only on last loop to allow file upload to finish

I have a main UI that has long processing tasks. I update a few hundred databases with information from a web server. The databases are stored in a repository where they need to be downloaded, then updated, then uploaded (checked back in) to the repository.
I am not so concerned with the length of work, but I have implemented a Background worker thread to handle the upload so that the next file can start processing. Each step of the process is logged to a couple of different XML log files for later analysis on what was updated in each file. Works great until...
My issue is that just before I loop I call the run Async background worker to handle the upload, works great until the last file. I have tried a dozen or more different techniques to block, IsBusy, join, sleep, etc. the main thread, in order to wait for only the last file to be uploaded before I can close up my XML writers, and confirm that all files were uploaded correctly.
I have also tried to implement the RunWorkerCompleted which fires okay, but only after returning to the main thread, so even if I set a flag or add logic to the last condition of the event, I get an "in use" error on my XML writer. Can't figure that one out either since I would have thought it was done updating.
I can post any code needed. Its a bit of spaghetti at this point, I guess I could use some help, direction, examples, of how to block the main thread on the last record of my loop only, so I can allow the final upload to finish. I really do not like just adding a Sleep as it just seems too inefficient and doesn't really respond when actual processing has completed.
vb.net
Main UI
For
' Processing ....
returnFiletoSS()
fileCountChker = fileCountChker + 1
Next
myXmlTextWriter.WriteEndElement()
myXmlTextWriter.Close()
myXmlTextWriter1.WriteEndElement()
myXmlTextWriter1.Close()
objWriter.Close()
' Prior test logic
' ''If fileCountChker = numOfFiles Then
' '' While TestWorker.IsBusy
' '' Threading.Thread.Sleep(400)
' '' End While
this fires before TestWorker_RunWorkerCompleted
If lastRecord Then
objWriter1.Close()
Else
While lastRecord <> True
Threading.Thread.Sleep(400)
End While
objWriter1.Close()
End If
calls this function...
Private Sub returnFiletoSS()
' This Background worker Process .
TestWorker = New BackgroundWorker
TestWorker.WorkerReportsProgress = True
TestWorker.WorkerSupportsCancellation = True
TestWorker.RunWorkerAsync()
End Sub
' This event handler deals with the results of the background operation.
Private Sub TestWorker_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles TestWorker.RunWorkerCompleted
' First, handle the case where an exception was thrown.
If (e.Error IsNot Nothing) Then
MessageBox.Show(e.Error.Message)
ElseIf e.Cancelled Then
' Next, handle the case where the user canceled the operation.
' Note that due to a race condition in the DoWork event handler,
' the Cancelled flag may not have been set, even though
' CancelAsync was called.
Form2.resultLabel("Canceled")
Else
' Finally, handle the case where the operation succeeded.
' Form2.resultLabel(e.Result.ToString())
lastRecord = True
' objWriter1.Close() 'Tried closing writer here, but errors.
End If

Updating UI while waiting for tasks to finish

I have a vb.net application processing a large amount of data. Due to the memory requirements of the process I am doing this batch-wise, with an overall planned structure as follows:
Do while Start < TotalNumberOfObjects
[cache data used for the upcoming batch]
For i = Start to Stop
[process data using multiple tasks...for example:]
t=taskfactory.startnew(doStuff(i))
TaskList.TryAdd(t.ContinueWith(Sub()
Me.BeginInvoke(DelegateUpdateProgress, {progress})
End Sub))
Next
[Wait for tasks to complete...
Normally I would wait for the tasks using task.waitall(),
but this will cause the UI to wait to update until all tasks are complete]
Start = Stop+1
Stop = Stop+Increment
[clear data from batch that was just completed]
loop
What's the proper way to:
Wait for all the tasks to complete before moving to the next batch?
Update the UI with the overall progress as each task completes?
My target framework is .NET 4.0.
I appreciate any input.
EDIT: Currently I am updating the UI upon completion of each task using task.continuewith() and calling me.beginInvoke to update the form,
TaskList.TryAdd(t.ContinueWith(Sub()
Me.BeginInvoke(DelegateUpdateProgress, {progress})
End Sub))
However, this is incompatible with how I would expect to wait for a list of tasks to complete, task.waitall(tasklist) because calling task.waitall will cause the UI thread to wait to update until all the tasks are complete.
First you need to set up a delegate and then use Dispatcher.Invoke
In the example below, a button is changed from enabled to disable (or the other way around):
Delegate Sub SetRecordButtonEnabledCallback(ByVal Enabled As Boolean)
Friend Sub SetRecordButtonEnabled(ByVal Enabled As Boolean)
Me.btnDGRecord.IsEnabled = Enabled
End Sub
after that all you need to do is call the following code from within your timer to invoke it:
Dim DesiredValue as Boolean = True
Me.Dispatcher.Invoke(New SetRecordButtonEnabledCallback(AddressOf SetRecordButtonEnabled), New
Object() {DesiredValue})
Why don´t you put your routine in a Backgroundworker structure?
So, while you code process data, you´re ready to update any UI component you have.
If necessary, you may also update UI from Backgroundworker, but you need to have some special requirements to do this.
You may consult here:
https://msdn.microsoft.com/en-us//library/ywkkz4s1.aspx

Waiting For Process To Complete

Is there any way to pause a process or wait unitl the process is complete before continuing onto the next line of code?
Here is my current process to zip all PDFs and then delete. Currently, its deleting files before the zipping is complete. Is there a way to pause/wait until the process is complete?
Dim psInfo As New System.Diagnostics.ProcessStartInfo("C:\Program Files\7-Zip\7z.exe ", Arg1 + ZipFileName + PathToPDFs)
psInfo.WindowStyle = ProcessWindowStyle.Hidden
System.Diagnostics.Process.Start(psInfo)
'delete remaining pdfs
For Each foundFile As String In My.Computer.FileSystem.GetFiles("C:\Temp\", FileIO.SearchOption.SearchAllSubDirectories, "*.pdf")
File.Delete(foundFile)
Next
Process.Start returns a Process instance. As others have mentioned, you can use the WaitForExit() method, although you should probably use WaitForExit(Integer), which includes a timeout for just in case something goes wrong with the zipping process.
So your code would become something like:
...
Dim zipper As System.Diagnostics.Process = System.Diagnostics.Process.Start(psInfo)
Dim timeout As Integer = 60000 '1 minute in milliseconds
If Not zipper.WaitForExit(timeout) Then
'Something went wrong with the zipping process; we waited longer than a minute
Else
'delete remaining pdfs
...
End If
You can use process.WaitForExit method
WaitForExit can make the current thread wait until the associated process to exit.
Link : http://msdn.microsoft.com/fr-fr/library/system.diagnostics.process.waitforexit(v=vs.80).aspx
There are several WaitForExit methods available.
Check out Process.WaitForExit.
WaitForExit() makes the current thread wait until the associated
process terminates. It should be called after all other methods are
called on the process. To avoid blocking the current thread, use the
Exited event.
Instructs the Process component to wait indefinitely for the
associated process to exit.
WaitForExit(Int32) makes the current thread wait until the
associated process terminates. It should be called after all other
methods are called on the process. To avoid blocking the current
thread, use the Exited event.
Instructs the Process component to wait the specified number of
milliseconds for the associated process to exit.