Reputation: 261
I am trying to use multi-processing in Python for face recognition on a Raspberry Pi. To make full use of all the 4 cores, I used the multi-thread concept. Below is part of my (pseudo-)code:
count = 1
while True:
image = cap.read
if count == 1:
r1 = pool.apply_async(func, [image]) # this is the image process module
output = r2.get() # this is used to get the results from processor #2
showimage(output) # show the processed results
elif count == 2:
r2 = pool.apply_async(func, [image]) # this is the image process module
output = r3.get() # this is used to get the results from processor #3
showimage(output) # show the processed results
elif count == 3:
r3 = pool.apply_async(func, [image]) # this is the image process module
output = r4.get() # this is used to get the results from processor #4
showimage(output) # show the processed results
elif count == 4:
r4 = pool.apply_async(func, [image]) # this is the image process module
output = r1.get() # this is used to get the results from processor #1
showimage(output) # show the processed results
count = 0
count += 1
I understand there will be some delay in showing the image compared to the actual image captures (three cycles). What I don't understand about running the algorithm is that there are certain levels of a getting-stuck phenomenon. It could be like the following:
OR
it could be any sequence starting from either r1, r2, r3, r4. I don't understand what causes this stuck thing? Can anybody help analyze? Thanks.
Below is the snapshot of profiling:
Upvotes: 0
Views: 119
Reputation: 366003
If you actually want to fire off the jobs as quickly as possible, and take the results as soon as they're available, without blocking the new jobs being fired, you have to stop interleaving them.
The simplest way to do this (assuming you want the results in order of task creation1) is probably to wait on a background thread, like this:
q = queue.Queue()
def handle():
while True:
res = q.get()
output = res.get()
showimage(output)
threading.Thread(target=handle)
while True:
image = cap.read
res = pool.apply_async(func, [image])
q.put(res)
This exact design may not work—e.g., if showimage
has to run on the main thread, you'll have to swap the two threads, and if cap.read
also has to run on the main thread you'll need to manage multiple queue and make everything more complicated—but it should show the idea.
1. If you want the results in whatever order they finish, it will instead probably be simpler to switch from a multiprocessing.Pool
to a concurrent.futures.ProcessPoolExecutor
, because it's easier to wait on a group of future
s than a group of AsyncResult
s. But there are other alternatives.
Upvotes: 0
Reputation: 742
Here's a simple example that explains the patterns you're seeing
Suppose you have 4 people with 4 empty glasses of water in front of them. Think of get() as "Finish drinking the water in your glass, i'm going to wait until you do before moving on". Think of apply_async as being "I'm going to fill up your glass, start drinking but I'm moving on".
So what happens:
count == 1
We fill person A's glass and they're drinking slowly
We wait for person B to finish their cup, it's already empty, we move on
count == 2
We fill person B's glass and they're drinking slowly
We wait for person C to finish their cup, it's already empty, we move on
...
count == 4
We fill person D's glass and they're drinking slowly
We wait for person A to finish their cup
OK so suppose A takes 30 seconds to finish their water but only takes us 5 seconds to do the steps above.
We're now going to be waiting 25 seconds for A to finish their drink before we move on. But with all that waiting time, person B, C and D also finished their drink, so once A is done, we zoom through the next 3 people, until we get back to A again.
Upvotes: 2