In addition to the MSDN Best Practices, I'll add:
- Don't make your own threads. Prefer to use the ThreadPool (or the new Task Parallel Library Tasks). Managing your own thread is rarely, if ever, the correct design decision.
- Take extra care with UI related issues. Control.Invoke (Windows Forms) and Dispatcher.Invoke (WPF), or use SynchronizationContext.Current with Post/Send
- Favor using the BackgroundWorker class when appropriate.
- Try to keep synchronization via locks to a minimum
- Make sure to synchronize everything that requires synchronization
- Favor the methods in the Interlocked class when possible over locking
Once you get more advanced, and are trying to optimize, other things to look for:
- Watch out for false sharing. This is especially problematic when working with arrays, since every array write to any element in an array includes a bounds check in .NET, which in effect causes an access on the array near element 0 (just prior to element 0 in memory). This can cause perf. to go downhill dramatically.
- Beware of closure issues, especially when working in looping situations. Nasty bugs can occur if you're closing on a variable in the wrong scope when making a delegate.