In my application I use the .NET SerialPort class for reading and writing data. The reading is done using the DataReceived event, I assume internally on a ThreadPool thread.
Here's a great thread on the topic, with the author of the SerialPort class participating:
MSDN: How does SerialPort handle DataReceived?
From my experience, I've written a dozen serial communication apps for use as hardware simulators, I don't lock. I didn't know at the time if I was safe or not, but in practice, I haven't had an error yet. (a year of near constant use by 20+ testers and automated test machines) That said, my applications don't leave the company, if I were writing apps for public consumption I might take more care.