Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

Best way to have an actor do background processing

I want to make an actor that would stream images from a camera, sending it to my view. I have a central Acquire method, that needs to run continuously, and that is synchronous: it waits for the camera to deliver the image, and sends the image to the view. It can be a long task (several 100 ms) 

Meanwhile the actor handles occasional "change acq parameters" message, which changes object's private data.

Without AF I can do this easily by setting the message dequeue's timeout to 0, and so when there's no message to handle I call Acquire.  With AF I have no control on the loop, so I see different options:

* fork Actor.lvclass, and modify the Actor core loop

* have a helper loop send "Acquire" messages to self, but the actor queue will grow too fast as Acquire is long. I don't want to hard code a wait time in this helper loop.

* make the private data a DVR, but I hate using DVRs for this kind of thing

So this leaves me with forking the actor class... Is there another alternative?

0 Kudos
Message 1 of 5
(5,058 Views)

I just deleted my previous post.

Never mind... I just realized you're actually aiming at a slightly different problem than Drew is. They're close, but not identical. His is a stream of tasks each of which takes a reasonable amount of time and needs to interleave background operations. Yours is a single large task that needs to be interrupted by another incoming task.

In your case, you're trying to have an actor that actually needs to do a interruptable task.

Option 1: Do the long task work in a parallel loop in Actor Core.vi. When your actor receives a message to do a long task, send that task over to the other loop (use a queue or something). In that other loop, poll a Notifier with a time out of zero to communicate new values when the main actor receives new parameters. The Notifier's "timed out?" terminal will let you quickly skip if no new values are available. The communication is entirely within one actor.

Option 2: Take advantage of the statefulness of the actor. Your caller will send you two messages: "Do Long Task" and "Change Parameters". Make your actor's contract with callers that "Do Long Task" will always be sent with Normal priority whereas "Change Parameters" will always be sent at high priority. When you receive "Do Long Task" message, start work on that task. Work for a short time, then save your state and enqueue a single high priority message to yourself to keep working. Then exit the method. You will then come around and either handle that message immediately -- so you pick up working where you left off -- or you will handle a "Change Parameters" message and then when that is done, you'll get your mesage. New long tasks will wait until you're through with all the high priority messages.

0 Kudos
Message 2 of 5
(3,793 Views)

CharlesB64 wrote:

Is there another alternative?

You could have "Acquire" enqueue itself as its last action, preferably with lower prioroty than incoming "change parameter" messages.  That would behave similar to a zero timeout.  But given the possibly long delays, a separate helper loop (as sugested by AQ) might be better

0 Kudos
Message 3 of 5
(3,793 Views)

Thanks AQ & drjdpowell. Calling acquire again, at the end of Acquire:Do, should nicely solve my problem.

I don't see the need of setting the "change parameter" message to a higher priority, since during one "Acquire:Do", the actor's queue is empty, as it's filled with "Acquire" once at a time. So, when caller sends "Change parameter", it is added in front of the queue, and "Acquire" is added behind it, so next the actor will execute "Change parameter", and "Acquire" again. Is it correct?

0 Kudos
Message 4 of 5
(3,793 Views)

CharlesB64 wrote:

I don't see the need of setting the "change parameter" message to a higher priority, since during one "Acquire:Do", the actor's queue is empty, as it's filled with "Acquire" once at a time. So, when caller sends "Change parameter", it is added in front of the queue, and "Acquire" is added behind it, so next the actor will execute "Change parameter", and "Acquire" again. Is it correct?

When dealing with a long task that is re-adding itself to the queue, sending parameter changes intended to take effect while that long task is still running at high priority is needed in some use cases but not others. It doesn't (generally) hurt the ones where it isn't needed, so it is generally a good practice to follow.

Consider if your "Acquire" task was "Acquire 10000 Points From Source X", which I will shorthand as AcquireX (and for the same message from a different data source, I will use AcquireY ... same message, different payload). I will shorthand "Change Parameters" as CP.

The caller sends the original AcquireX, and your actor acquires 100 points and then re-enqueues itself. Then the caller sends AcquireY with instructions to acquire from a different data source, Y. Your message queue now looks like this:

(back of queue, front of queue)

High priorty: <empty>

Normal priority: AcquireY, AcquireX

So your actor now acquires another 100 points from X and then re-enqueues.

High priorty: <empty>

Normal priority: AcquireY, AcquireX

And you realize that's not right because now the AcquireY has interrupted AcquireX before AcquireX was fully finished. So you abort your run and modify your VI to do the re-enqueue at High priority. Now your queue looks like this:

High priorty: AcquireX

Normal priority: AcquireY

And so X will now completely finish all 10000 points before Y starts. But your sender is still sending CP at Normal priority. So if it sends CP, that gets stuck in the queue behind AcquireY.

High priorty: AcquireX

Normal priority: CP, AcquireY

But what you really want is for CP to jump ahead and get involved even while AcquireX is still running. So you tell your sender to send it at high priority. Now it works correctly:

High priorty: CP, AcquireX

Normal priority: AcquireY

The trick is figuring out whether re-enqueue of AcquireX should be at High or Normal priority. That will depend upon whether or not you want it to behave like a single long task ahead of other long task operations or not.

0 Kudos
Message 5 of 5
(3,793 Views)