Try not to hold locks across long operations like I/O where performance can be adversely affected.
Don't hold locks when calling a function that is outside the module and that might reenter the module.
In general, start with a coarse-grained approach, identify bottlenecks, and add finer-grained locking where necessary to alleviate the bottlenecks. Most locks are held for short amounts of time and contention is rare, so fix only those locks that have measured contention.
When using multiple locks, avoid deadlocks by making sure that all threads acquire the locks in the same order.