When calling AlsaMidiInput.Dispose() or AlsaMidiInput.CloseInputAsync(), the underlying AlsaSequencer keeps a thread alive via a task running EventLoop() that never completes. In order to dispose properly, it should call AlsaSequencer.StopListening(), wait for the event loop to complete, then dispose the rest of the resources. It's important to wait for the event loop to finish before calling AlsaSequencer.Dispose(), because AlsaSequencer.Dispose() makes the event loop hang as it's polling for data. I am unable to compile the solution on my end on linux so I'm using reflection to perform these actions. By using this method, I can confirm that the thread is no longer alive after AlsaMidiInput.Dispose().