We generally adopt an ETL approach and copy our data into 'worktables' created by our multithreaded app. Each thread creates a worktable ( generated by guid ) in a work database. The data are bulk copied to the worktable and then a procedure to load the work table to the target table is executed to finish the load. Worktables are dropped at the end of the load. The load procedure then lets you code against deadlock situations.
Obviously a big change in method and performance from directly loading into a table, but we don't encounter any deadlocks this way and we often have 15 threads running at a time.
Regards
Dave