Hi,
I’m working on a problem that involves tasks communicating through channels.
I have a task that needs to read from two channels and I’m trying to figure out the best way of doing that.
The task needs to read an incoming sequence of video frames and buffer the last 30 seconds worth of frames in an internal array. These frames are read from channel ch1
.
At the same time, there is another incoming channel (ch2
) which is used to notify the task that an event of interest has been detected (the detection is performed by another task, which sends notifications through the channel ch2
).
When a notification comes along in channel ch2
, the task has to use the buffered frames to create a video file. My question is: what’s the best way of implementing this? Is it even a good idea to have a task read from more than one channel?
One important point is that the update rate in channels ch1
and ch2
are, most of the time, quite different. Channel ch1
is fed regularly with frames at a rate of frate
frames per second (typically 2 or 3 fps). In turn, channel ch2
can go hours or days with no activity at all. However, it could also happen that a detection happens for every frame, in which case channel ch2
would be updated at the same rate as channel ch1
.
The first thing I could try is this:
using Base.Threads: @spawn
ch1 = Channel(32)
ch2 = Channel(32)
function buffer_frames_1()
for item_1 in ch1
...
for item_2 in ch2
...
end
end
end
This doesn’t work though, since once we get to the inner loop, we get stuck in there and never get a chance to read values from channel ch1
in the outer loop again.
Another possibility is:
function buffer_frames_2()
while true
item_1 = take!(ch1)
item_2 = take!(ch2)
...
end
end
This is better, but it doesn’t really work either because, since take!()
blocks when the channel is empty, the two channels are read at the rate of the slower channel. Besides, I think this means the higher rate channel will fill up.
A third approach involves using isready()
(which is non-blocking) on the channels before calling take!()
(which is blocking):
function buffer_frames_3()
while true
if isready(ch1)
item_1 = take!(ch1)
...
end
if isready(ch2)
item_2 = take!(ch2)
...
end
end
end
This works in the sense that the channels are read at the proper rate, but it results in a high CPU usage, probably because the while
loop is executed zillions of times a second. A possible solution would be to insert a sleep()
call in this loop so that it could be executed only slightly faster than the faster channel (maybe 2*frate
times per second) but at this point I’m not sure I’m going in the right direction anymore.
One more solution I thought of was to use just one channel and use a flag to signal that a detection happened. In this case, the task sending the video frames would insert flag=0
in the channel. In turn, the task sending the detection notifications would send empty frames and insert flag=1
in the channel when a detection happened. This would avoid the problem of having to read from two channels.
I wonder if there are better, more elegant solutions to this problem. Any help is greatly appreciated.