02-01-2023 01:41 PM
I've seen two ways to wire the Queue Refnum in an event-based producer-consumer environment. This is from the LabVIEW template:
The queue reference is given into the loop by simple tunnel and used after the loop is done by simply wiring around it. The following snippet is showing another way:
This time, the queue reference is wired via a shift register and after the last loop iteration forwarded to the queue release.
Are there any differences between both approaches or downsides? Obviously the second way involves more wiring making the diagram less readable in my opinion. Thanks for the clarification!
02-01-2023 02:26 PM
You've hit it already. Less opportunity for forgetting to wire in the top example, since tunnels on event structures default to not needing to be wired and then *poof* away goes access to the queue reference.
I tend to avoid wiring through structures for reference types just to save myself some wiring. If I have direct control over when a loop will terminated (that is, no error into the stop terminal) then I'll wire through but only wire up the output on the terminating case and don't use a shift register.
02-01-2023 03:29 PM
I take the general approach that anything that goes in a shift register in a loop is something that could change from iteration to iteration.
Unless you have multiple queues that you're switching between, or creating and destroying them as you go, a queue reference never changes and is therefore something that shouldn't go in a shift register.
One small other thing is that in either of those situations, I would recommend that the consumer loop be the one that triggers the closing of the reference as a general rule. If the producer loop finishing destroys the reference, then there's a chance that there will be unexecuted consumer loop actions that never happen. If that's what you want, enqueue a "stop" action to the front of the queue to get the same effect. Some producer/consumer loops treat the destruction of the queue as a "stop" action but that can lead to skipped queue elements, and it's not hard at all to just add "stop" as one more action.
02-01-2023 08:35 PM - edited 02-01-2023 08:38 PM
In regard to destroying the reference. Here is something that might help down the road. You'll notice there is a condition where the producer loop might complete while the consumer is still running. When the producer is stopped/finished, the reference is destroyed. The "side-effect" of destroying the reference while the consumer is still running can be used to the programmers advantage. The VI waiting in the consumer will generate an error once the reference to it is destroyed. The error output of the VI can be wired and used to stop the consumer loop. Kind of a handy "feature."
02-02-2023 02:51 AM
@BigApple0 wrote:
In regard to destroying the reference. Here is something that might help down the road. You'll notice there is a condition where the producer loop might complete while the consumer is still running. When the producer is stopped/finished, the reference is destroyed. The "side-effect" of destroying the reference while the consumer is still running can be used to the programmers advantage. The VI waiting in the consumer will generate an error once the reference to it is destroyed. The error output of the VI can be wired and used to stop the consumer loop. Kind of a handy "feature."
But the big disadvantage is that anything that was in the queue isn't there any more, meaning data loss. Maybe this is fine for you... or maybe you are wondering why you are missing the last few points of data... or even worse, maybe you don't even realize you are missing the last few points of data.
To avoid potential data loss, always send an explicit "exit" message to the consumer. I used to do what you mentioned here until someone told me what I just told you.
02-02-2023 07:30 AM
@billko wrote:
@BigApple0 wrote:
In regard to destroying the reference. Here is something that might help down the road. You'll notice there is a condition where the producer loop might complete while the consumer is still running. When the producer is stopped/finished, the reference is destroyed. The "side-effect" of destroying the reference while the consumer is still running can be used to the programmers advantage. The VI waiting in the consumer will generate an error once the reference to it is destroyed. The error output of the VI can be wired and used to stop the consumer loop. Kind of a handy "feature."
But the big disadvantage is that anything that was in the queue isn't there any more, meaning data loss. Maybe this is fine for you... or maybe you are wondering why you are missing the last few points of data... or even worse, maybe you don't even realize you are missing the last few points of data.
To avoid potential data loss, always send an explicit "exit" message to the consumer. I used to do what you mentioned here until someone told me what I just told you.
I have the general rule (applies 99.8% of the time) that the consumer is the one in charge of the queue reference. The consumer needs to be told when to stop and it can then destroy the queue when it completes.
02-02-2023 09:10 AM
@crossrulz wrote:
@billko wrote:
@BigApple0 wrote:
In regard to destroying the reference. Here is something that might help down the road. You'll notice there is a condition where the producer loop might complete while the consumer is still running. When the producer is stopped/finished, the reference is destroyed. The "side-effect" of destroying the reference while the consumer is still running can be used to the programmers advantage. The VI waiting in the consumer will generate an error once the reference to it is destroyed. The error output of the VI can be wired and used to stop the consumer loop. Kind of a handy "feature."
But the big disadvantage is that anything that was in the queue isn't there any more, meaning data loss. Maybe this is fine for you... or maybe you are wondering why you are missing the last few points of data... or even worse, maybe you don't even realize you are missing the last few points of data.
To avoid potential data loss, always send an explicit "exit" message to the consumer. I used to do what you mentioned here until someone told me what I just told you.
I have the general rule (applies 99.8% of the time) that the consumer is the one in charge of the queue reference. The consumer needs to be told when to stop and it can then destroy the queue when it completes.
Your explanation was so much more elegant than mine. Yes, this is what I was trying to get across. Consumer loop exits gracefully, destroy the queue after loop exits. If you want to be even more thorough, you can merge the errors from producer and consumer loops before destroying the queue just to make certain everyone's absolutely ready to have the queue destroyed.
02-02-2023 09:41 AM
@billko wrote:
If you want to be even more thorough, you can merge the errors from producer and consumer loops before destroying the queue just to make certain everyone's absolutely ready to have the queue destroyed.
From a cohesiveness aspect, that only makes sense when dealing with the simple Producer/Consumer and everything in on the same block diagram. Once you start going into "actors" or "message handlers" (really they are the same concept), you really want to use libraries to create these boundaries. Part of that setting the boundaries is having the queue lifetime on the consumer VI. This is something Actor Framework did extremely well.
At this point, it is more of a readability and SOLID issue than functionality.
02-02-2023 10:24 AM
@Kyle97330 wrote:
I take the general approach that anything that goes in a shift register in a loop is something that could change from iteration to iteration.
Unless you have multiple queues that you're switching between, or creating and destroying them as you go, a queue reference never changes and is therefore something that shouldn't go in a shift register.
One small other thing is that in either of those situations, I would recommend that the consumer loop be the one that triggers the closing of the reference as a general rule. If the producer loop finishing destroys the reference, then there's a chance that there will be unexecuted consumer loop actions that never happen. If that's what you want, enqueue a "stop" action to the front of the queue to get the same effect. Some producer/consumer loops treat the destruction of the queue as a "stop" action but that can lead to skipped queue elements, and it's not hard at all to just add "stop" as one more action.
There is one issue with this approach and that is if your wire a reference through a For loop using just a terminal and need the reference output from that For loop you will run into problems if the loop executes 0 times. If the For loop doesn't execute, such as the iteration value if 0 or the array driving the loop is empty, the reference will be lost. The output from the For loop will be an invalid reference, not what you wired into the loop.
02-02-2023 10:50 AM - edited 02-02-2023 10:53 AM
@Mark_Yedinak wrote:
@Kyle97330 wrote:
I take the general approach that anything that goes in a shift register in a loop is something that could change from iteration to iteration.
Unless you have multiple queues that you're switching between, or creating and destroying them as you go, a queue reference never changes and is therefore something that shouldn't go in a shift register.
One small other thing is that in either of those situations, I would recommend that the consumer loop be the one that triggers the closing of the reference as a general rule. If the producer loop finishing destroys the reference, then there's a chance that there will be unexecuted consumer loop actions that never happen. If that's what you want, enqueue a "stop" action to the front of the queue to get the same effect. Some producer/consumer loops treat the destruction of the queue as a "stop" action but that can lead to skipped queue elements, and it's not hard at all to just add "stop" as one more action.
There is one issue with this approach and that is if your wire a reference through a For loop using just a terminal and need the reference output from that For loop you will run into problems if the loop executes 0 times. If the For loop doesn't execute, such as the iteration value if 0 or the array driving the loop is empty, the reference will be lost. The output from the For loop will be an invalid reference, not what you wired into the loop.
Just to expand a little on that thought, the same thing happens to anything that "feeds through" the FOR loop. If it iterates zero times, you get the default for the datatype out, unless it's in a shift register, which "carries" the original value to the "other side".