The standard way of doing this is using pipe opens. man perlipc
has some examples - the example uses communication between the parent and a single child, but filehandles can easily be saved in a datastructure.
Note that if you use pipe-open, open returns the PID of the child. To determine a child terminating, set up a signal handler to catch SIGCLD. This signal doesn't tell you the pid, but if you do a wait or a waitpid in the handler, you do. Given the PID of the terminated child, you know which task was assigned to it (assuming you stored this information when dealing out the tasks).
Study man perlipc, it's a real treasure trove. It should have enough examples to get you on your way.