-
Apr 16th, 2017, 11:06 AM
#1
ways to provide a synchronous response from an asynchronous task
Originally Posted by Schmidt
There are dozens of different ways, to ensure "synchronous-behaviour for asynchronous tasks"
('Sleep 1 + DoEvents' being only one of those "non-GUI-blocking Wait-Functions")...
[...]
- how to wait for the finishing of these threads within a function that behaves synchronously on the outside
Olaf
Yes, that's the question.
The asynchronous component usually notifies that it finished with an event, but there could be also a property that one can check to see whether it finished or not, for example Object.StillExecuting
So, the DoEvents way for waiting for the asynch task to complete is:
Code:
Do While Object.StillExecuting
DoEvents
[May be also] Sleep Somevalue
Loop
What are the others?
-
Apr 16th, 2017, 11:52 AM
#2
Re: ways to provide a synchronous response from an asynchronous task
DoEvents leads to other issues if you fail to address them: user can close the form, access menus and other controls.
What is the goal here? Prevent user from continuing until async action finishes? Provide some sort of status while async is executing? Something else?
-
Apr 16th, 2017, 12:17 PM
#3
Re: ways to provide a synchronous response from an asynchronous task
Originally Posted by LaVolpe
DoEvents leads to other issues if you fail to address them: user can close the form, access menus and other controls.
That can be easily secured by disabling Controls (or group of controls by disabling their Container) -
or by setting Cancel in Form_QueryUnload etc...
Originally Posted by LaVolpe
What is the goal here? Prevent user from continuing until async action finishes? Provide some sort of status while async is executing?
The question is precise enough and doesn't leave much room for interpretation IMO.
It's the question, how one can write (or place) e.g. a function(call) like:
TimeNeeded = CalculateMandelBrotAreaOn(VBPictureBox)
... in ones normal program-flow - which will then not return until the task is finished ...
(although one was e.g. splitting the area into 4 SubAreas - then starting 4 threads on each SubArea
asynchronously, not knowing in what order the 4 threads wil be finished with their SubArea-calculations,
but waiting until all 4 of them "are done").
@Eduardo
the DoEvents-call should be the last one, before checking "cancel-conditions" though, so it would be better to write:
Code:
Do While Object.StillExecuting And Not (boolCancelFlag Or TimeOutReached)
Sleep SomevalueLargerThanZero '<- this should be mandatory in such loops
DoEvents 'last call before the Exit-Condition-Check
Loop
Olaf
Last edited by Schmidt; Apr 16th, 2017 at 12:23 PM.
-
Apr 16th, 2017, 12:23 PM
#4
Re: ways to provide a synchronous response from an asynchronous task
Olaf. The reason for asking for clarification is that the OP also stated that he could check the state in some cases? Therefore, why check for the state if a completion event is sent? So, yes there is room for interpretation IMO. Usually a DoEvents is in place because a) no final notification is provided or b) a status wants to be displayed or c) the ability to cancel the async action.
Another option could be to show a modal form. In that form, the ability to close it is disabled. The code that begins the async exists in that form. If the user can't close the form, only the completion event triggers it closure, then the user is stuck until the form closes.
-
Apr 16th, 2017, 12:39 PM
#5
Re: ways to provide a synchronous response from an asynchronous task
Originally Posted by Schmidt
@Eduardo
the DoEvents-call should be the last one, before checking "cancel-conditions"
Of course. I tried to present it in a minimalistic example.
Originally Posted by Schmidt
though, so it would be better to write:
Code:
Do While Object.StillExecuting Or boolCancelFlag Or TimeOutReached
Sleep SomevalueLargerThanZero '<- this should be mandatory in such loops
DoEvents 'last call before the Exit-Condition-Check
Loop
Olaf
Why do you say that Sleep should be mandatory?
AFAIK, if there is no Sleep, the processor will show 100% usage in the task manager, even when it's in fact not really used (or something like that), but aside from that there is not other issue (AFAIK).
On the other hand, Sleep blocks any execution in the current thread for that time, so if the task you are waiting for is done in the same thread (in VB code in the same project), what you are doing with the Sleep call is to freeze the execution for that little while, and so making the task to take a little longer than it would otherwise.
A small price that perhaps is better to pay so the task manager doesn't show the processor running at 100%.
But if the task is performed in another thread, I agree that the Sleep should be mandatory. (But even whithout it, I think you won't notice much difference in performance).
Is there anything else involved?
-
Apr 16th, 2017, 12:53 PM
#6
Re: ways to provide a synchronous response from an asynchronous task
Originally Posted by LaVolpe
Olaf. The reason for asking for clarification is that the OP also stated that he could check the state in some cases? Therefore, why check for the state if a completion event is sent? So, yes there is room for interpretation IMO. Usually a DoEvents is in place because a) no final notification is provided or b) a status wants to be displayed or c) the ability to cancel the async action.
Another option could be to show a modal form. In that form, the ability to close it is disabled. The code that begins the async exists in that form. If the user can't close the form, only the completion event triggers it closure, then the user is stuck until the form closes.
Hello LaVolpe,
The issue is to return a synchronous result from an asynchronous task.
Of course you can do it whith an event, like:
Code:
Private Withevent SomeObject as cObjectClass
Private mFinished as Boolean
Private mLeaving as Boolean
Public Funtion RunAsynchTaskAndReturnResult() as ResultType
mFinished = False
SomeObject.RunAsyncTask
Do While Not mFinished
Sleep 1
DoEvents
iF mLeaving Then Exit Sub
Loop
RunAsynchTaskAndReturnResult = SomeObject.Result
End Function
Private Sub SomeObject_Finished()
mFinished = True
End Sub
Private Sub Form_Unload(Cancel As Integer)
If Cancel = 0 Then
mLeaving = True
End If
End Sub
But if the object has a property to indicate whether it finished or not, it is the same than doing:
Code:
Private mLeaving as Boolean
Public Funtion RunAsynchTaskAndReturnResult() as ResultType
SomeObject.RunAsyncTask
Do While SomeObject.Running ' Or .StillExecuting, or whatever
Sleep 1
DoEvents
iF mLeaving Then Exit Sub
Loop
RunAsynchTaskAndReturnResult = SomeObject.Result
End Function
Private Sub Form_Unload(Cancel As Integer)
If Cancel = 0 Then
mLeaving = True
End If
End Sub
It already has the "mFinished" condition implemented in a property.
Last edited by Eduardo-; Apr 16th, 2017 at 12:57 PM.
-
Apr 16th, 2017, 01:00 PM
#7
Re: ways to provide a synchronous response from an asynchronous task
Originally Posted by Eduardo-
Why do you say that Sleep should be mandatory?
AFAIK, if there is no Sleep, the processor will show 100% usage in the task manager, even when it's in fact not really used (or something like that), but aside from that there is not other issue (AFAIK).
Well, you pointed out the issue already - without an additional Sleep-call, the CPU-Core goes to 100% Load
(and thus will drain your NoteBook-battery unnecessarily, or making your CPU-cooler make unnecessary noise etc.).
Originally Posted by Eduardo-
On the other hand, Sleep blocks any execution in the current thread for that time, so if the task you are waiting for is done in the same thread (in VB code in the same project), what you are doing with the Sleep call is to freeze the execution for that little while, and so making the task to take a little longer than it would otherwise.
Not when you reduce the Sleep-interval by wrapping the loop in e.g.:
- timeBeginPeriod 1
- timeEndPeriod 1
... or use one of the Waitxxx-APIs instead of Sleep
... or use SleepEx, when your async tasks are based on CompletionPorts, etc.
That's what I meant with "dozens of different ways to wait for async completion" -
if there's no GUI involved, there's even more APIs one could choose from (avoiding DoEvents completely).
Though - if there's a GUI involved, you will in most cases have to do it by "opening up a new Message-Pump"
(and a DoEvents-looping does exactly that - as does LaVolpes suggestion, to show a Modal-Form instead).
Olaf
-
Apr 16th, 2017, 01:02 PM
#8
Re: ways to provide a synchronous response from an asynchronous task
@Eduardo. Well I will bow out of this thread. In the 1st example, you are using a DoEvents loop which unless you disabled your form/controls, the user is allowed to continue/abort while waiting on the async event to finish. If the user wasn't allowed, then the loop would be unneeded since no action would be allowed until the completed event was sent. That is the only part of your question that confused me. I read the question as ways of making async behave as sync, I interpreted it as meaning: the user waits, period, no interaction is allowed. Clearly I misunderstood, which is why I asked for clarification
Edited: And for a little clarification on my part
When I said above the loop wouldn't be needed, the assumption is that you have that code related to a modal window. Otherwise, you have little choice in using a loop, of some sort, while waiting for the async event to finish while trying to restrict some user actions. Whether it is necessary to query the state property/value is a separate item. Modal windows are a common approach -- think of a progress bar. If the user is allowed to cancel the async action, then the progress bar window will have a 'cancel' button else it won't. The modal window approach would negate any need for a loop if not allowed to abort. However, I can't imagine any good application that would force a user to wait for an async action without ability to abort on demand if the action was taking too long or user changed their mind.
Last edited by LaVolpe; Apr 16th, 2017 at 01:30 PM.
-
Apr 16th, 2017, 01:33 PM
#9
Re: ways to provide a synchronous response from an asynchronous task
Yes, there are two different scenarios, or may be three:
1) The user is compelled to wait for the function to return the response and can do nothing else.
2) This is a variation from 1: the user must wait for the function to return, but the only other thing that he can do is to cancel the task.
3) The user can do anything, to call other functions (launch other tasks), close the application, cancel the task, call the same function again while the previous response is still in course.
To disable all the controls/forms can serve for option 1.
To wrap a form displayed modally can be a good option for option 2.
But if you don't want to be so limiting, and you want to give freedom to the user while he is executing a long task, as a professional program in my opinion should do, option 3 is the best one.
Besides, if it is a component that any client program could use, I don't see 1 and 2 as nice options.
So the issue is to handle reentrancy. But that's another subject and would be off-topic, but it's related.
The subject here is how to do a synchronous function to return a result from an asynchronous task.
(Leaving aside how to handle reentrancy)
Besides the use of DoEvent, another way have been suggested: to wrap a form displayed modally (may be outside of the screen so it's not visible, or may be a 1x1 pixel borderless form, so the user don't see it).
It has the side effect that option 3 is not viable.
-
Apr 16th, 2017, 01:42 PM
#10
Re: ways to provide a synchronous response from an asynchronous task
Well, option 3 doesn't really meet the definition of synchronous, does it? I think we all agree Option 1 is not very good, unless you are updating some software or the system maybe. And option 2, whether done in the current form's context, or within a progress-like form, is where I think this thread is headed.
For a separate component, I'd imagine you might be tracking the status of the event and forwarding the event to the user. In that case, your component simply initiates, repeats/forwards events, allows caller to trigger a cancel option, and informs caller that event has finished normally or canceled.
Reentrancy is not off-topic IMO. It must be addressed if it will be a problem whenever you are using a loop that allows it.
Tip: I don't think you can move windows off the desktop any longer. And if the user had the ability to cancel the process, it shouldn't be hidden.
-
Apr 16th, 2017, 01:47 PM
#11
Re: ways to provide a synchronous response from an asynchronous task
FWIW, here's a bit of quick&dirty code, which shows an alternative to Sleep in the WaitLoop (with fast reaction time to UserInput),
and also how one can handle "premature Form-Closing" (when the task is still running).
Into a Form (a Form_Click will cancel the WaitLoop - and trying to close the Form whilst waiting will also be handled)
Code:
Option Explicit
Private Declare Function MsgWaitForMultipleObjects Lib "user32" (ByVal nCount As Long, pHandles As Long, ByVal fWaitAll As Long, ByVal dwMilliseconds As Long, ByVal dwWakeMask As Long) As Long
Private bCancel As Boolean
Private Sub Form_Load()
Show
Caption = "Waiting..."
PerformAsyncTaskUntilCompletion
Caption = "Wait finished"
If Tag = "x" Then Unload Me
End Sub
Private Sub Form_Click()
bCancel = True
End Sub
Private Sub PerformAsyncTaskUntilCompletion()
Do While Not bCancel
MsgWaitForMultipleObjects 0, ByVal 0&, 0&, 15, &H4FF '<- QS_ALLINPUT
DoEvents
Loop
End Sub
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
If Caption = "Waiting..." Then
Cancel = 1
bCancel = MsgBox("Async-Job still running, do you want to cancel it?", vbYesNo) = vbYes
If bCancel Then Tag = "x"
End If
End Sub
Olaf
-
Apr 16th, 2017, 01:51 PM
#12
Re: ways to provide a synchronous response from an asynchronous task
Similar to what Olaf provided, I typically use a form/class-level variable that contains bitwise flags. Where a value of zero indicates no looping is going on, 1 indicates looping is active, 2 indicates user attempting to cancel but not yet canceled, and 4,8,16, etc are used for other custom purposes. That way, the variable can be checked in any event to allow or prevent reentrancy, i.e., If (m_IsBusy And 1) Then looping & respond accordingly.
But to the question at hand, if not placing the async event behind a modal form and you have no other way to generate a synchronous event, then a loop may be your only answer. Disabling the entire form/component until the event finishes isn't ideal either as that leads to confusion for the user.
-
Apr 16th, 2017, 02:10 PM
#13
Re: ways to provide a synchronous response from an asynchronous task
Originally Posted by LaVolpe
Well, option 3 doesn't really meet the definition of synchronous, does it?
Synchronous calls (which don't return, until async "Sub-Jobs" are done) allow an
easier formulation of WorkFlows.
E.g. one "WorkFlow-task" could be, to synchronize local DB content with an online-DB.
Sub SynchronizeWithOnlineDB()
... CheckForDifferences
... DownloadDiffAndUpdateLocal
... UploadDiffAndUpdateOnline
... CheckForDifferences 'again, to make sure they really are zero now on both ends
End Sub
When each one of the 4 Sub-Tasks above is written as a routine with synchronous behaviour,
the aggregating routine (SynchronizeWithOnlineDB) can be written in a "normal way" -
then in turn perhaps just updating a "SideBar" with the current progress of the WorkFlow
(whilst the User is allowed to work normally on his local DB).
Sure, one can trigger the workflow-actions also directly from asynchronously incoming Events,
but sometimes a lot of different "Event-Throwers" are involved and a lot of Flags- to manage
when to start other parts of the over-all-workflow...
For some devs this might not be as "clean" a solution as the alternative...
Olaf
-
Apr 16th, 2017, 02:21 PM
#14
Re: ways to provide a synchronous response from an asynchronous task
@Olaf. Maybe semantics are at play here. From my posts, I am using the definitions: When you execute something synchronously, you wait for it to finish before moving on to another task. When you execute something asynchronously, you can move on to another task before it finishes. In other words, a synchronous call is similar to: MsgBox "Hello World". Any lines of code that follow will not execute until the msgbox is closed. Eduardo's option 3, IMO, is a great definition of async vs sync.
-
Apr 16th, 2017, 02:29 PM
#15
Re: ways to provide a synchronous response from an asynchronous task
Originally Posted by LaVolpe
Well, option 3 doesn't really meet the definition of synchronous, does it?
Yes, synchronous (as I understand it) means that it doesn't return to the caller until it has the result.
It is not about not being able to do another thing while waiting.
That's what can be confusing, that it's still inside a function (it didn't return or exit the function) but still some code started to run somewhere else.
And the code that started to run somewhere else can be also synchronous or asynchronous.
If it is asynchronous, it start another task, may be in another thread, or some task that runs in the same thread but run in chunks and is called by a timer that is started when you start that task.
In this way, in the case of an asynch task, the first task (our main task we are talking about here) will run along with this second one.
But if this second task is also synchronous, it will run and finish first, and our first task won't be reasumed (at least it won't be reasumed our checking whether it finised in the case the task is running in other thread and has already finished) until this second task finish.
It will behave in a LIFO way (last in first out).
Because DoEvents allows to go to process events, but it doesn't return until the events are all processed. It's not that it will do a little of one job and and a little of the other.
Originally Posted by LaVolpe
I think we all agree Option 1 is not very good, unless you are updating some software or the system maybe. And option 2, whether done in the current form's context, or within a progress-like form
Agree.
Originally Posted by LaVolpe
is where I think this thread is headed.
In my intention it is headed to option 3.
But we could discuss any option because I think the subject is interesting enough.
Originally Posted by LaVolpe
For a separate component, I'd imagine you might be tracking the status of the event and forwarding the event to the user.
Exactly.
Originally Posted by LaVolpe
In that case, your component simply initiates, repeats/forwards events, allows caller to trigger a cancel option, and informs caller that event has finished normally or canceled.
It doesn't have any UI element, the cancel order must come from the client program.
Originally Posted by LaVolpe
Reentrancy is not off-topic IMO. It must be addressed if it will be a problem whenever you are using a loop that allows it.
Yes, but reentrancy is tight related to the nature of the functions, what you can actually do and what you can't. I mean, it's related to the logic and nature of the program.
For example, if you are writing to a file, you cannot allow to write to the same file while the firt task is not completed... or yes in the case it's a database and it's another register.
If you are calling a web page for the weather information, and there is another call for the weather information while the fist one still didn't return (may be for the same geographic location or for another one), there is no problem with that reentrancy, you should be able to return both results.
Originally Posted by LaVolpe
Tip: I don't think you can move windows off the desktop any longer. And if the user had the ability to cancel the process, it shouldn't be hidden.
If you want to show UI, yes.
-
Apr 16th, 2017, 02:33 PM
#16
Re: ways to provide a synchronous response from an asynchronous task
Originally Posted by Schmidt
FWIW, here's a bit of quick&dirty code, which shows an alternative to Sleep in the WaitLoop (with fast reaction time to UserInput),
and also how one can handle "premature Form-Closing" (when the task is still running).
Into a Form (a Form_Click will cancel the WaitLoop - and trying to close the Form whilst waiting will also be handled)
Code:
Option Explicit
Private Declare Function MsgWaitForMultipleObjects Lib "user32" (ByVal nCount As Long, pHandles As Long, ByVal fWaitAll As Long, ByVal dwMilliseconds As Long, ByVal dwWakeMask As Long) As Long
Private bCancel As Boolean
Private Sub Form_Load()
Show
Caption = "Waiting..."
PerformAsyncTaskUntilCompletion
Caption = "Wait finished"
If Tag = "x" Then Unload Me
End Sub
Private Sub Form_Click()
bCancel = True
End Sub
Private Sub PerformAsyncTaskUntilCompletion()
Do While Not bCancel
MsgWaitForMultipleObjects 0, ByVal 0&, 0&, 15, &H4FF '<- QS_ALLINPUT
DoEvents
Loop
End Sub
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
If Caption = "Waiting..." Then
Cancel = 1
bCancel = MsgBox("Async-Job still running, do you want to cancel it?", vbYesNo) = vbYes
If bCancel Then Tag = "x"
End If
End Sub
Olaf
But you are still using DoEvents.
And second: what is the point os using the MsgWaitForMultipleObjects API when you have already that information in a variable (or a property)?
-
Apr 16th, 2017, 02:52 PM
#17
Re: ways to provide a synchronous response from an asynchronous task
That's what can be confusing, that it's still inside a function (it didn't return or exit the function) but still some code started to run somewhere else.
That's understandable if DoEvents is used. And of course even without DoEvents, if any code inside that function called another method that could trigger an event, then that event could be executing other code.
It doesn't have any UI element, the cancel order must come from the client program.
Yes. Your component will usually have a cancel method or pass a boolean via an event to allow the user to set that boolean, on return, in order to cancel.
Almost sounds like you may be more interested in an ActiveX EXE?
-
Apr 16th, 2017, 02:59 PM
#18
Re: ways to provide a synchronous response from an asynchronous task
Originally Posted by Eduardo-
But you are still using DoEvents.
Because there's no easier way to fire-up a (fully vbRuntime-compatible) MessagePump (MessageLoop) in VB,
Originally Posted by Eduardo-
And second: what is the point os using the MsgWaitForMultipleObjects API ...
It's a better replacment for the Sleep-call (to avoid the already mentioned 100% CPU-utilization,
which is caused because DoEvents deals with the Apps MessageQueue "sidewards" per PeekMessage).
In the example, despite having specified a TimeOut of 15msec there (similar to a Sleep 15),
the call to MsgWaitForMultipleObjects will *immediately* return (before the 15msec are up),
when a new "general-input" WindowsMessage is detected in the Apps MessageQueue.
A Sleep 1 will not return until 12-15msec are up (unless you introduced the already mentioned timeBeginPeriod/EndPeriod-calls in addition).
So, what I've posted is an immediately reacting MessagePump, which still will avoid the "100% CPU-load issue"
(in case one is careless enough to use DoEvents in a "naked way" inside such a MessagePump-loop).
Olaf
-
Apr 16th, 2017, 03:01 PM
#19
Re: ways to provide a synchronous response from an asynchronous task
Originally Posted by LaVolpe
That's understandable if DoEvents is used. And of course even without DoEvents, if any code inside that function called another method that could trigger an event, then that event could be executing other code.
Yes, and if the client calls the same function again from the event procedure it could end up with an out of stack space error.
Originally Posted by LaVolpe
Yes. Your component will usually have a cancel method or pass a boolean via an event to allow the user to set that boolean, on return, in order to cancel.
Almost sounds like you may be more interested in an ActiveX EXE?
No... it is an OCX.
And since AX exe's can't be used in SxS installations (that is what I always use now) it's ruled out for me.
I never made an AX exe BTW.
-
Apr 16th, 2017, 03:17 PM
#20
Re: ways to provide a synchronous response from an asynchronous task
Don't know if this could apply, but maybe have your ocx be a container for another ocx. That inner ocx is created as a control array where each does its own thing and forwards events to your outer ocx. Kinda like a form hosting an array of Winsock controls.
-
Apr 16th, 2017, 03:24 PM
#21
Re: ways to provide a synchronous response from an asynchronous task
Originally Posted by LaVolpe
Don't know if this could apply, but maybe have your ocx be a container for another ocx. That inner ocx is created as a control array where each does its own thing and forwards events to your outer ocx. Kinda like a form hosting an array of Winsock controls.
That still doesn't solve the problem which this thread is all about:
How to write a synchronous function which:
- triggers several async actions (which will later report back over Events)
- only returns, after all async actions were finished
I've described the problem already in post #3...
So (to get specific) what would be your solution to:
TimeNeeded = CalculateMandelBrotAreaOn(VBPictureBox)
TimeNeeded being calculated inside the (synchronously behaving) "outer function" (CalculateMandelBrotAreaOn) -
the function itself internally triggering 4 asynchronous actions (4 threads) which will calculate something (on 4 Cores in parallel).
Olaf
-
Apr 16th, 2017, 03:31 PM
#22
Re: ways to provide a synchronous response from an asynchronous task
Originally Posted by Schmidt
Because there's no easier way to fire-up a (fully vbRuntime-compatible) MessagePump (MessageLoop) in VB,
It's a better replacment for the Sleep-call (to avoid the already mentioned 100% CPU-utilization,
which is caused because DoEvents deals with the Apps MessageQueue "sidewards" per PeekMessage).
In the example, despite having specified a TimeOut of 15msec there (similar to a Sleep 15),
the call to MsgWaitForMultipleObjects will *immediately* return (before the 15msec are up),
when a new "general-input" WindowsMessage is detected in the Apps MessageQueue.
A Sleep 1 will not return until 12-15msec are up (unless you introduced the already mentioned timeBeginPeriod/EndPeriod-calls in addition).
So, what I've posted is an immediately reacting MessagePump, which still will avoid the "100% CPU-load issue"
(in case one is careless enough to use DoEvents in a "naked way" inside such a MessagePump-loop).
Olaf
So all that is to avoid the 15 ms that in some cases Sleep may waste.
But how to know what object handle to provide, for example in the case of an WinHTTPReuqest call I would not know where to go to get a handle.
Anyway, in that particular case there is already that property WaitForResponse that can be used instead of the loop.
In my real case I have some tasks that are driven by a timer, so what I need to wait is that the timer is not enabled any more.
So, the task is something similar to this:
Code:
Private mStep As Long
Private mCancelled as Boolean
Public Function GetSomething (nSomeParameter) as cWhatever
tmrDoTask.Interval = 1
mStep = 0
tmrDoTask.Enabled
Do Until Not tmrDoTask.Enabled
Sleep 1 ' I doubt it's neccesary here
DoEvents
Loop
Set GetSomething = mResult
End Function
Private Sub tmrDoTask_Timer()
DoTask
End Sub
Private Sub DoTask()
Static sFinished As Boolean
If mStep = 1 Then
Goto Step1
ElseIf mStep = 2 Then
Goto Step2
ElseIf mStep = 3 Then
Goto Step3
End If
' the task begins here
' do initializing and first things
mStep = 1
Exit Sub
Step1:
If mCancelled Then tmrTask.Enabled = False: Exit Sub
sFinished = False
Do Until sFinished
' do something
mStep = 2
Exit Sub
Step2:
If mCancelled Then tmrTask.Enabled = False: Exit Sub
' do some other thing
If SomeCondition then sFinished = true
mStep = 3
Exit Sub
Step3:
Loop
Set GetSomething = [Some Result] ' Set result
tmrTask.Enabled = False
End Sub
Last edited by Eduardo-; Apr 16th, 2017 at 04:43 PM.
-
Apr 16th, 2017, 03:39 PM
#23
Re: ways to provide a synchronous response from an asynchronous task
@Olaf. My reply to Eduardo was pertaining to his most recent reply to me.
There are very few options and unless someone wants to create some thunk or COM solution to act as an async>sync go-between. At no point did I read that the requirement was to perform several async tasks, maybe I missed it, maybe there is some history on this thread we readers are not privy to? If a requirement was to wait for multiple simultaneous async requests to finish before returning, then that method obviously would have to track & wait on each task. Encapsulating those into a single class, control, whatever, could be another solution to simplify. But we may be interpreting this thread differently
-
Apr 16th, 2017, 03:54 PM
#24
Re: ways to provide a synchronous response from an asynchronous task
Originally Posted by LaVolpe
Don't know if this could apply, but maybe have your ocx be a container for another ocx. That inner ocx is created as a control array where each does its own thing and forwards events to your outer ocx. Kinda like a form hosting an array of Winsock controls.
I don't understand how that would help.
The OCX's all run in the same thread that the client application, I don't see any advantage in doing that.
May be I'm missing something.
And for sub-tasks I use class modules.
Last edited by Eduardo-; Apr 16th, 2017 at 04:00 PM.
-
Apr 16th, 2017, 04:05 PM
#25
Re: ways to provide a synchronous response from an asynchronous task
Originally Posted by LaVolpe
...At no point did I read that the requirement was to perform several async tasks, maybe I missed it,
I guess you did, because the threads title already describes the problem fully:
How to: "provide a synchronous response from an asynchronous task"
a "synchronous response" (IMO) cannot be misunderstood as anything different than: "being the result of a synchronous (method-) call"
The exact count of "async tasks to perform" was not specified but even if it's exactly 1, the problem is still the same:
- async actions usually report their results in an Event
- so, how to *wait* (inside the outer synchronous function) until that Event comes in
Originally Posted by LaVolpe
maybe there is some history on this thread we readers are not privy to?
I'm quite certain, the prior thread which caused the question here was: http://www.vbforums.com/showthread.p...pload-progress
But the problem is an old one - everyone who has written Rs232-stuff with the MSComm-OCX for serial interfaces,
or is using the Winsock-OCX for his own TCP-stuff has the problem to write e.g. a function like this one here:
Result = SendTCPRequest("SomeInputString") 'expecting and returning some OutPutString from the TCPServer-response (which came in over an Event)
Olaf
Last edited by Schmidt; Apr 16th, 2017 at 04:08 PM.
-
Apr 16th, 2017, 04:16 PM
#26
Re: ways to provide a synchronous response from an asynchronous task
Well, at least it helped me to be more secure that I'm not doing something stupid, because I know that a lot of people hate DoEvents, some even say it should never be used, but the fact is that so far, for my goal, there is not other way to do this.
If anyone reading this knows that there is an intrinsic bug in DoEvent, please explain it.
What I understand is that sometimes it's difficult to handle reentrancy properly, but there is no bug with DoEvents itself.
Of course that it can cause problems, because virtually anything can happen (and change the state) after a DoEvents call.
-
Apr 16th, 2017, 04:39 PM
#27
Re: ways to provide a synchronous response from an asynchronous task
I don't think anyone has suggested there is any "bug" in the DoEvents() function.
It is just a very costly and hazardous option, and one that can be avoided in most but not all cases. DoEvents() does a Sleep 0 in addition to the many other things it does. People often try to reduce its cost by adding a longer Sleep() next to it so it gets called less frequently, or they'll try only making the call every so many iterations of the busy-wait loop. Disabling most of the user interface helps avoid some of the more minor re-entrance time bombs.
Typically:
If you have some sort of request/response scenario, you dispatch your async request and then in your async response event handler you process the response when it arrives. Pretty simple.
If the response can trigger another request, then issue the subsequent request in the response event handler. This normally means tracking state as you go so that when subsequent responses arrive the code knows which it is dealing with.
In some cases you might build a work queue of multiple requests, then initiate the first one. As responses arrive they get processed and then the next request is issued. This continues until the work queue is empty.
It sounds like you are committed to a QBasic-style straight line code pattern. To force-fit it you plan to use buzz loops with DoEvents() calls. Sure seems like a house of straw to me, but I don't think anyone is going to dissuade you.
-
Apr 16th, 2017, 04:39 PM
#28
Re: ways to provide a synchronous response from an asynchronous task
The pseudo code a bit more complete, now handling reentrancy:
Code:
Private mStep As Long
Private mCancelled As Boolean
Public Function GetSomething(nSomeParameter) As cWhatever
' if there is a previous task running, then wait until it finishes
' in this example we run only one task at a time
Do Until Not tmrDoTask.Enabled
DoEvents
If mCancelled Then Exit Function
Loop
If Not (nSomeParameter = mLastCallParameter) Then ' if yes, we already have the result ready from the last call
tmrDoTask.Interval = 1
mStep = 0
tmrDoTask.Enabled
Do Until Not tmrDoTask.Enabled
Sleep 1 ' I doubt it's neccesary here
DoEvents
If mCancelled Then Exit Function
Loop
End If
Set GetSomething = mResult
End Function
Private Sub tmrDoTask_Timer()
DoTask
End Sub
Private Sub DoTask()
Static sFinished As Boolean
If mStep = 1 Then
GoTo Step1
ElseIf mStep = 2 Then
GoTo Step2
ElseIf mStep = 3 Then
GoTo Step3
End If
' the task begins here
' do initializing and first things
mStep = 1
Exit Sub
Step1:
If mCancelled Then tmrTask.Enabled = False: Exit Sub
sFinished = False
Do Until sFinished
' do something
mStep = 2
Exit Sub
Step2:
If mCancelled Then tmrTask.Enabled = False: Exit Sub
' do some other thing
If SomeCondition Then sFinished = True
mStep = 3
Exit Sub
Step3:
Loop
Set GetSomething = [Some Result] ' Set the result
tmrTask.Enabled = False
End Sub
Public Sub Cancel()
mCancelled = True
End Sub
-
Apr 16th, 2017, 04:43 PM
#29
Re: ways to provide a synchronous response from an asynchronous task
Originally Posted by Eduardo-
So all that is to avoid the 15 ms that in some cases Sleep may waste.
Yes, some async actions (e.g when you wait for the response of a TCPServer sitting in a LAN or on localhost)
are finished within 1-2msec.
In that case you would seriously limit the overall-responsetime of your outer (synchronous) function,
when you are using Sleep 15 (instead of using MsgWaitxxx or some similar API).
Originally Posted by Eduardo-
But how to know what object handle to provide, ...
The MsgWaitxxx... example (as posted) doesn't need to be provided with a Handle -
it works generically in such loops (when used as shown) - as a faster reacting alternative to the Sleep-API.
Olaf
-
Apr 16th, 2017, 04:49 PM
#30
Re: ways to provide a synchronous response from an asynchronous task
Originally Posted by dilettante
It sounds like you are committed to a QBasic-style straight line code pattern. To force-fit it you plan to use buzz loops with DoEvents() calls. Sure seems like a house of straw to me, but I don't think anyone is going to dissuade you.
Off course you can dissuade me!!!
You can come up with your house of bricks and modern code (not QB-like) and tell me how would you provide a synchronous response from an asynchronous task given the conditions and goals already explained here, and of course, without any call to DoEvents.
-
Apr 16th, 2017, 04:56 PM
#31
Re: ways to provide a synchronous response from an asynchronous task
Originally Posted by Schmidt
The MsgWaitxxx... example (as posted) doesn't need to be provided with a Handle -
it works generically in such loops (when used as shown) - as a faster reacting alternative to the Sleep-API.
Olaf
Ahh, OK, it's like Sleep but it returns early if there is any event.
Not I understand. It is good to know about it.
I had see that function but I thought that it worked only with handles.
Thanks.
-
Apr 16th, 2017, 05:02 PM
#32
Re: ways to provide a synchronous response from an asynchronous task
Originally Posted by Eduardo-
(not QB-like)
I think dilettante is talking about the GoTo's, another thing that some people hate and sometimes are very useful or even neccesary.
Of course that routine could be redesigned to avoid the GoTo's, but I don't have that problem because I'm not a GoTo hater.
-
Apr 16th, 2017, 05:50 PM
#33
Re: ways to provide a synchronous response from an asynchronous task
Originally Posted by Eduardo-
I think dilettante is talking about the GoTo's, another thing that some people hate and sometimes are very useful or even neccesary.
Of course that routine could be redesigned to avoid the GoTo's, but I don't have that problem because I'm not a GoTo hater.
I'm not a GoTo-hater but It still doesn't read very nicely (at least to me)...
Here's a working example, on how I'd do your "multi-step"-async-stuff:
Into a Class, named cAsyncWrap:
Code:
Option Explicit
Private Declare Function MsgWaitForMultipleObjects Lib "user32" (ByVal nCount As Long, pHandles As Long, ByVal fWaitAll As Long, ByVal dwMilliseconds As Long, ByVal dwWakeMask As Long) As Long
Public Enum eAsyncTaskState
tsk_None
tsk_InitDone
tsk_Step1
tsk_Step2
tsk_Step3
End Enum
Private Type tAsyncTask
State As eAsyncTaskState
Cancelled As Boolean
TimedOut As Boolean
FinalResult As Variant
ErrorString As String
End Type
Private mTask As tAsyncTask
Public Function GetSomething(nSomeParameters)
If mTask.State <> tsk_None Then Err.Raise vbObjectError, , "a task is still running"
On Error GoTo 1
DoInits nSomeParameters
DoStep tsk_Step1
DoStep tsk_Step2
DoStep tsk_Step3
GetSomething = mTask.FinalResult 'provide the Result
1 mTask.State = tsk_None 'reset the State in either case (failure or not)
mTask.ErrorString = Err.Description 'reflect the error-string (if there is one)
End Function
Public Sub Cancel()
mTask.Cancelled = True
End Sub
Public Property Get State() As eAsyncTaskState
State = mTask.State
End Property
Public Property Get LastErrorString() As String
LastErrorString = mTask.ErrorString
End Property
'some Private Helper-Routines
Private Sub DoInits(SomeInitParameters)
mTask.FinalResult = Empty 'reset a few mTask.Members before the whole thing starts
mTask.Cancelled = False
mTask.TimedOut = False
mTask.ErrorString = ""
'do some other prior Class-Initializations (according to the passed Params)
mTask.State = tsk_InitDone 'reflect the State-Increase (from prior state tsk_None)
End Sub
Private Sub DoStep(NextState As eAsyncTaskState)
Select Case NextState
Case tsk_Step1: 'trigger async actions for Step1 here (the Events will ensure that the mTask.State is increased)...
Case tsk_Step2: 'trigger async actions for Step2 here (the Events will ensure that the mTask.State is increased)...
Case tsk_Step3: 'trigger async actions for Step3 here (the Events will ensure that the mTask.State is increased)...
End Select
WaitFor NextState
End Sub
Private Sub WaitFor(NextState As eAsyncTaskState)
Do Until mTask.Cancelled Or mTask.TimedOut Or mTask.State = NextState
MsgWaitForMultipleObjects 0, ByVal 0&, 0&, 15, &H4FF '<- QS_ALLINPUT
DoEvents
Loop
If mTask.Cancelled Then Err.Raise vbObjectError, , "Task was cancelled at step: " & NextState
If mTask.TimedOut Then Err.Raise vbObjectError, , "Task timed out at step: " & NextState
End Sub
Thanks to the above class, the Form-Code is then reduced to:
Code:
Option Explicit
Private AsyncWrap As New cAsyncWrap
Private Sub Form_Load()
Show
Caption = "Waiting..."
Dim Result: Result = AsyncWrap.GetSomething("foo")
Caption = "Wait finished"
If Tag = "x" Then Unload Me
End Sub
Private Sub Form_Click()
AsyncWrap.Cancel
If AsyncWrap.State Then Print "Cancelled at state: " & AsyncWrap.State
End Sub
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
If AsyncWrap.State <> tsk_None Then
Cancel = 1
If MsgBox("Async-Job still running, do you want to cancel it?", vbYesNo) = vbYes Then
AsyncWrap.Cancel
Tag = "x"
End If
End If
End Sub
Olaf
-
Apr 16th, 2017, 06:52 PM
#34
Re: ways to provide a synchronous response from an asynchronous task
Yes Olaf, I already said that it could be refactored to avoid the GoTo's, but they are there because I just modified a previous version of the function that used DoEvents inside instead of the Exit Sub's and the timer.
But it could not handle reentrancy, that's why I made the changes.
In my example the Step 1 would run only once, but the Steps 2 and 3 would run multiple times in a loop, I don't see that in yours.
Also, your example relies in that the routine would trigger some asynch actions in every step, and it could be the case... or not...
My example with the timer would work also in the case that every step performs a chunk of the whole task, even when these chunks are synchronous tasks each one.
The asynchronous task in my example is the task my routine performs in chunks, not necessarily the individual actions that it triggers.
But aside from that, I don't see that yours could handle reentrancy properly (and Lavolpe was right about that the two subjects are closely related).
Why?
In fact, the only thing you do with reentrancy is to raise an error.
While (in my last example) I wait the prior task to finish and see if the result is the same that I need now, and if it isn't then I run another task. No error.
Code:
Public Function GetSomething(nSomeParameter) As cWhatever
' if there is a previous task running, then wait until it finishes
' in this example we run only one task at a time
Do Until Not tmrDoTask.Enabled
DoEvents
If mCancelled Then Exit Function
Loop
If Not (nSomeParameter = mLastCallParameter) Then ' if yes, we already have the result ready from the last call
'...
[PS: OK, I could have put If (nSomeParameter <> mLastCallParameter) Then instead of If Not (nSomeParameter = mLastCallParameter) Then]
The whole problem was in order to allow reentrancy, if I don't allow reentrancy just a few DoEvents inside the loop will do it (to unlock the UI), as I had done before.
There are already traces of how the routine was originally, just remove the Exit Sub's and replace them with DoEvents, and of course remove the GoTo's and the mStep variable.
Because what are the goals? as the title says: to return a synchronous result of an asynch task. And your code does that. Using DoEvents.
But aside that it uses DoEvents, that seems already unavoidable, you can't handle reentrancy.
Why? Because when you enter the function for the second time, it is because it comes from some DoEvents call inside the same function, and you can't wait for the first call to complete with another Loop and DoEvent call because it will never return to the first DoEvent call. The second time it will wait forever.
DoEvents will only work if there is any event to occur, that's where the timer comes into play.
Last edited by Eduardo-; Apr 16th, 2017 at 06:55 PM.
-
Apr 16th, 2017, 08:08 PM
#35
Re: ways to provide a synchronous response from an asynchronous task
Here is a version without GoTo's, and yes, it's more clear:
Code:
' It uses tmrDoTask, a Timer
Private mStep As Long
Private mCancelled As Boolean
Private mLastCallParameter
Private mResult as cWhatever
Public Function GetSomething(nSomeParameter) As cWhatever
' if there is a previous task running, then wait until it finishes
' in this example we run only one task at a time
Do Until Not tmrDoTask.Enabled
DoEvents
If mCancelled Then Exit Function
Loop
If nSomeParameter <> mLastCallParameter Then ' if they are equal we already have the result ready from the last call
mStep = 0
DoTask
Do Until Not tmrDoTask.Enabled
Sleep 1 ' I doubt it's necessary here
DoEvents
If mCancelled Then Exit Function
Loop
End If
Set GetSomething = mResult
mLastCallParameter = nSomeParameter
End Function
Private Sub tmrDoTask_Timer()
DoTask
End Sub
Private Sub DoTask()
Static sFinished As Boolean
If mStep = 0 Then
sFinished = False
' the task begins here
' do initializing and first things
mStep = 1
tmrDoTask.Interval = 1
tmrTask.Enabled = True
ElseIf sFinished Then
Set mResult = [Some Result] ' Set the result
tmrTask.Enabled = False
ElseIf mStep = 1 Then
If mCancelled Then tmrTask.Enabled = False: Exit Sub
' do something
mStep = 2
ElseIf mStep = 2 Then
If mCancelled Then tmrTask.Enabled = False: Exit Sub
' do some other thing
mStep = 1
If SomeCondition Then sFinished = True
End If
End Sub
Public Sub Cancel()
mCancelled = True
End Sub
I didn't add error trace code for the sake of simplicity.
Last edited by Eduardo-; Apr 16th, 2017 at 08:18 PM.
-
Apr 16th, 2017, 10:51 PM
#36
Re: ways to provide a synchronous response from an asynchronous task
I realize that there is a problem with the above code.
It is not as my real case, because in my real case if the input parameter changes, I abort the previous task. So I don't have that problem in my real case.
But the above example wouldn't work right.
The problem is here:
Code:
' if there is a previous task running, then wait until it finishes
' in this example we run only one task at a time
Do Until Not tmrDoTask.Enabled
DoEvents
If mCancelled Then Exit Function
Loop
If nSomeParameter <> mLastCallParameter Then ' if they are equal we already have the result ready from the last call
' ...
If the parameter in the second call is the same as the one in the first, there will be no problem, the second call will retun first and then inmediately will return the first call with the right result also because both are the same.
But if the parameter in the second call changes, the second call will return with the right result and the first call will return with the result of the second.
To address that issue it would be neccesary to add a collection or array to hold the results already processed and their input parameters.
Something like this:
Code:
' It uses tmrDoTask, a Timer
Private mStep As Long
Private mCancelled As Boolean
Private mResults as New Collection
Private mCurrentCallParameter
Public Function GetSomething(nSomeParameter) As cWhatever
On Error Resume Next
Set GetSomething = mResults(CStr(nSomeParameter))
On Error Goto 0
If Not GetSomething Is Nothing then ' We had already this result
Exit Function
End If
If tmrDoTask.Enabled Then
' if there is a previous task running, then wait until it finishes
' in this example we run only one task at a time
Do Until Not tmrDoTask.Enabled
DoEvents
If mCancelled Then Exit Function
Loop
' Let's try again, perhaps the last call that just finished has set the right result that we need in this call already
On Error Resume Next
Set GetSomething = mResults(CStr(nSomeParameter))
On Error Goto 0
If Not GetSomething Is Nothing then ' The previous call did it
Exit Function
End If
End If
mCurrentCallParameter = nSomeParameter
mStep = 0
DoTask
Do Until Not tmrDoTask.Enabled
Sleep 1 ' I doubt it's necessary here
DoEvents
If mCancelled Then Exit Function
Loop
End If
Set GetSomething = mResults(CStr(nSomeParameter))
End Function
Private Sub tmrDoTask_Timer()
DoTask
End Sub
Private Sub DoTask()
Static sFinished As Boolean
If mStep = 0 Then
sFinished = False
' the task begins here
' do initializing and first things
mStep = 1
tmrDoTask.Interval = 1
tmrTask.Enabled = True
ElseIf sFinished Then
mResults.Add [Some Result], CStr(mCurrentCallParameter) ' Set the result
tmrTask.Enabled = False
ElseIf mStep = 1 Then
If mCancelled Then tmrTask.Enabled = False: Exit Sub
' do something
mStep = 2
ElseIf mStep = 2 Then
If mCancelled Then tmrTask.Enabled = False: Exit Sub
' do some other thing
mStep = 1
If SomeCondition Then sFinished = True
End If
End Sub
Public Sub Cancel()
mCancelled = True
End Sub
Last edited by Eduardo-; Apr 16th, 2017 at 11:04 PM.
-
Apr 17th, 2017, 07:02 PM
#37
Re: ways to provide a synchronous response from an asynchronous task
Originally Posted by Eduardo-
In my example the Step 1 would run only once, but the Steps 2 and 3 would run multiple times in a loop, I don't see that in yours.
Well, it'd be easy enough to put a loop around "Steps 2 and 3" if you want that -
I was not trying to write an 1:1 replacement for your code - it was just a bunch of suggestions
(because I don't really see the need of a Timer in your scenario).
Originally Posted by Eduardo-
Also, your example relies in that the routine would trigger some asynch actions in every step,
If you need normal non-async-routines, it'd be equally easy to define and call them within the larger workflow -
can't see the problem really... (there already is a synchronous routine in the workflow - "DoInits", it
would be easy enough to e.g. introduce a DoSynchronousStep between DoStep 1 and DoStep 2)
Originally Posted by Eduardo-
But aside from that, I don't see that yours could handle reentrancy properly...
It is properly handled in so far, that it doesn't allow it (by raising an error to the outside -
and thus immediately leaving the function - denying any "stacking" of such calls).
Anything other than immediately jumping out of such a (still processing, still waiting to finish the prior task)
routine, is asking for trouble - so why not "suppress" it already in the GUI-code (by handling the error nicely,
offering to cancel the prior action in case the new one has "rights of ways").
Olaf
-
Apr 17th, 2017, 08:06 PM
#38
Re: ways to provide a synchronous response from an asynchronous task
OK, never mind.
My real code needs to handle reentrancy, not to raise an error.
But it's already doing so, with the timer.
The timer is necessary if you don't mind to "ask for trouble".
But there is no trouble that I can see.
You can study the last code that I posted if you are interested in undersdtanding how I solved it and why the timer is necessary.
Last edited by Eduardo-; Apr 17th, 2017 at 08:40 PM.
-
Apr 19th, 2017, 01:48 AM
#39
Re: ways to provide a synchronous response from an asynchronous task
Originally Posted by Eduardo-
OK, never mind.
My real code needs to handle reentrancy, not to raise an error.
But it's already doing so, with the timer.
The timer is necessary if you don't mind to "ask for trouble".
But there is no trouble that I can see.
You can study the last code that I posted if you are interested in undersdtanding how I solved it and why the timer is necessary.
I've looked at it - and the last version is even worse than the previous ones.
There's so many things wrong with it - I don't know where to start, seriously (I'm not trying to wind you up here)...
I guess if you'd try to make a real Demo out of your "air-code" (something that works,
using some Form-Code plus some Class-Code - something which mimicks your real scenario as best as it can),
you'd see it yourself easily.
Until then I also bow out of this discussion - and from what you wrote last - maybe LaVolpe was right,
in what you *really* want to do, is "to handle multiple asynchronous Jobs parallel" -
and for that purpose the threads topic (how to write a synchronous functions which waits for async results)
is not the proper approach - because it would be "stack-based" (and "stack-up", when called multiple times before being finished)...
I can only speculate that in your real scenario, you have to handle several uploads in parallel or something...
and for that a Queue-based approach (not a Stack-based one as the thread is about) would be the better solution.
And a Queue-based approach is better handled in a truly asynchronous manner (e.g. by a Class
which just offers an "AddJob" Method on the outside - then placing these Jobs in an internal Queue-Collection,
and then handling everything it has in that Queue internally - raising Events for the Jobs which are finished,
and just "idling" when nothing is in the Queue anymore). And that approach would be doable without any
DoEvents, just using a Class-internal timer...
Olaf
-
Apr 19th, 2017, 03:05 AM
#40
Re: ways to provide a synchronous response from an asynchronous task
Originally Posted by Schmidt
I've looked at it - and the last version is even worse than the previous ones.
There's so many things wrong with it - I don't know where to start, seriously (I'm not trying to wind you up here)...
I don't see enything wrong with it.
And since you are not pointing one single problem that you say you see...
Originally Posted by Schmidt
I guess if you'd try to make a real Demo out of your "air-code" (something that works, using some Form-Code plus some Class-Code - something which mimicks your real scenario as best as it can),
you'd see it yourself easily.
Until then I also bow out of this discussion - and from what you wrote last - maybe LaVolpe was right,
in what you *really* want to do, is "to handle multiple asynchronous Jobs parallel" -
and for that purpose the threads topic (how to write a synchronous functions which waits for async results)
is not the proper approach - because it would be "stack-based" (and "stack-up", when called multiple times before being finished)...
I can only speculate that in your real scenario, you have to handle several uploads in parallel or something...
and for that a Queue-based approach (not a Stack-based one as the thread is about) would be the better solution.
And a Queue-based approach is better handled in a truly asynchronous manner (e.g. by a Class
which just offers an "AddJob" Method on the outside - then placing these Jobs in an internal Queue-Collection,
and then handling everything it has in that Queue internally - raising Events for the Jobs which are finished,
and just "idling" when nothing is in the Queue anymore). And that approach would be doable without any
DoEvents, just using a Class-internal timer...
Olaf
No, they are not uploads.
The uploads are queued, as you said, and they have nothing to do with this. They are another part of the program and I not working on that yet.
As I explained somewhere else in this thread, the synchronous functions works in order LIFO, as everything that is synchronous would do.
There is no other way to do it anyway (if one wants to stay with synchronous functions).
Real code I don't have to post, propose something and I'll do it, I have no problem.
I would prefer something very close to the example I posted.
But I think the example I already posted should serve to understand the logic.
I can comment it more if you want.
I guess you could do more or less the same launching new threads, but I'm doing this without launching new threads (and without adding dependency files).
My real code is not too similar to this sample pseudocode.
In my real case I have many different functions. When they are called, most of the functions (but not all) need to have an "environment" already set up. And doing this can take some time (several seconds).
I do it the fist time that one of those functions are called.
The problem is that while the program is doing this, while waiting for some asynch calls (WinHTTP requests) to finish and also for not locking the client program, I need to call DoEvents, and since I do it, in that time I could receive a call to other of these functions (while I'm setting up the environment).
This second function needs also that enviroment ready, but that task was triggered already by the first function, so what can I do? Since these functions are all synchronous to the client program, I just need to wait until the environment is ready. Not only for the fist function that triggered it, but now also for the second.
Of course, I could add a method PrepareEnvironment that the client program must call and wait until it finishes before calling any of these functions, but I wanted to automate that.
I hope I didn't confuse you more. My sample posted here I think is easier.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|