Task almost never needs to be detached. Use just regular Task as default. Task.detached is equal of using DISPATCH_BLOCK_DETACHED flag in GCD (of which I wasn't aware until Swift introduced this in Task). But this looses priority, task locals, etc.
I would prefer synchronous operation body, it is usually easier. Since operation queue manages all separately from Swift Concurrency, you can synchronize your async operations if needed with quite of a confidence.
Plus, note that in either way – be it Task or DispatchQueue – you have the initial problem of start and cancel possibly called on separate threads, therefore you would need to synchronize access to state and check for "re-entrancy".
You generally want to design it so that missed cancellation isn't that critical otherwise – because there is just no way you'll be able to deterministically define that.
Note from the side thread, I see now why you don't have an access to the OSAllocatedUnfairLock. You can look up a similar implementation, the most practical one would be Protected from Alamofire and use in the same manner. But in general even just NSLock is definitely available and solves the task.