See also #2135.
I'm looking into the leak in #2105, and I have a theory that it's due to
callbacks being stored in this cache, but sometimes not being dequeued,
so leaking all references in them.
This assertion will only run in RC tests, and will ensure that this
doesn't happen.
We don't actually need to share a single queue to synchronize all the callback caches.
This new implementation also avoids using the risky `DispatchQueue.sync`.
This also fixes potential issues because `cachedCallbacksByKey` was exposed, and it could have been read without synchronizing access with the queue. This is now impossible.