I have solved this problem - but the solution seems odd to me, and I am concerned I am still doing something wrong.
If I create the q using the context returned from mp.get_context(‘spawn’), and use the torch.multiprocessing module to make the actual spawn call, everything works fine.
import torch.multiprocessing as mp

def f(i, q):
    msg(f"in f(): {q} {q.empty()}")
    msg(f"{q.get()}")

if __name__ == '__main__':
    smp = mp.get_context('spawn')
    q   = smp.SimpleQueue()
    q.put(['hello'])
    p   = mp.spawn(f, (q,))

I tried this because the torch.multiprocessing.spawn() call uses the same result of the same call for it’s start() call. It seems reasonable that the q code has to change depending on sharing mechanism, which will change whether this is forks or threads. Still have to use spawn() from torch.multiprocessing because the context does not have a spawn method.
But I am thoughtful apparently nobody else has this problem. I have not seen anything about this in any on line forums (fora?), and nothing in the docs. Is it really possible I am the only person to run into this?
Again, any commentary from more knowledgable users would be welcome.
s