What is the best way to delay code execution in an application while the program is still running?
I've tried this method:
System.Threading.Thread.Sleep()
But the application does not display until the Sleep() has been executed and I am unsure of alternatives.
Dim t = System.Threading.Tasks.Task(Of Boolean).Run(Function() As Boolean
System.Threading.Thread.Sleep(10000)
Return True
End Function)
Dim ret = t.Result
You need to offload this to another thread, if you call sleep on the current thread then everything including the UI is going to wait until sleep has finished. There are multiple ways to achieve this including the example above.
Related
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.
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.
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
I'm trying to figure why my form freezes up when executing some code. I also can't minimize or move the form. Is it because of the WaitForExit being used in the process?
The below code is tied to a button click.
If Checkbox1.checked = True Then
Call Test()
End If
If Checkbox2.checked = True Then
Goto NextStep
Else
Goto StopProcessing
End If
Here is the test sub I'm calling. Calls an exe with an optional argument.
Using psinfo As New Process
psinfo.StartInfo.FileName = "C:\Temp\Test.exe "
psinfo.StartInfo.Arguments = Arg1
psinfo.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
psinfo.Start()
psinfo.WaitForExit()
End Using
The WaitForExit was added (so I thought) to not process the next statement (next statement being the If statement for Checkbox2) until the process was complete. Is this not the case?
The WaitForExit was added (so I thought) to not process the next statement (next statement being the If statement for Checkbox2) until the process was complete.
When you call WaitForExit, it will block until the process (Test.exe) completes.
Since you're running this on the user interface thread, it will cause your form to "freeze" until the process completes fully.
If you need this to not occur, you would need to wait on a background thread. You could, potentially, move this code into a BackgroundWorker and use it to synchronize with your main window - but you will need to handle "waiting" for the process to finish in a different manner (ie: disable your UI up front, run the process, re-enable when complete).
Note that, with the Process class, another alternative would be to add EnableRaisingEvents on the process, then adding a handler to Process.Exited. This will let you not WaitForExit(), but instead get notified via an event when the process completes.
This Question has 2 parts. I am new to multithreading and so I want to firstly check if my logic is correct and then I want to find out how to handel erros in multithreading.
Q1: I have an application that calls SQL database obtaining information from 2 datatables, this info is then combined in a final display. Without multithreading, I call each SQL select to populate a dataset one after the other. With multithreading I call the more complex SQL first as a separate thread and then the less complex SQL call in the main thread second. I am trying to cut down the load time of both by doing them concurently.
(I realise that strictly I should do both as backround tasks to free up the UI, for me its small steps first)
Anyway the code looks little like this
Dim ThreadLoad_Longer_Data As Thread
ThreadLoad_Longer_Data = New Thread(AddressOf Me.Fill_LongerSQL)
ThreadLoad_Longer_Data.IsBackground = True
TThreadLoad_Longer_Data.Start()
'Execute some code here for the second SQL call in main thread
'Then stop the main prosess to wait for the finish of the of the background
ThreadLoad_Longer_Data.join
Im assuming that the .Join statment will infact stop the main thread and will wait for the other one to finish ? Is this correct ?
If so it brings me to the second part.
Q2. What happens if the first thread dosent finish? Like through an error ? How do I handle this situation ?
Thank you
Yes, calling ThreadLoad_Longer_Data.Join will stop the execution of the calling thread (the one that executes the code calling the Join) till the ThreadLoad_Longer_Data ends its execution.
If, inside ThreadLoad_Longer_Data, you have an unhandled exeception, the result is the ending of the thread and thus the resume of the execution of the calling thread.
Sub Main
Try
Console.WriteLine("Start of the main thread")
Dim ThreadLoad_Longer_Data As Thread
ThreadLoad_Longer_Data = New Thread(AddressOf Me.Fill_LongerSQL)
ThreadLoad_Longer_Data.IsBackground = True
ThreadLoad_Longer_Data.Start()
ThreadLoad_Longer_Data.Join
Console.WriteLine("End of the main thread")
Catch x as Exception
Console.WriteLine(x.Message)
End Try
End Sub
Sub Fill_LongerSQL()
Console.WriteLine("Before the exception")
dim y as integer
for x = 0 to 1000000000
y = y + 1
next
Throw new Exception("This is an unhandled exception")
' this will never executed
Console.WriteLine("After the exception")
End Sub