If you find this post interesting I strongly recommend looking at how Go, Clojure's core.async, and similar CSP systems attempt to address these issues.
One big advantage of a good CSP model is that you can express push/pull sensibly because you have a (buffered) channel abstraction between communicating processes.