While thread based locks can solve this, there is a manner which works across threads, but is probably best used when you have multiple processes writing to the end of a single file.
To get this behavior across processes (or threads too), specify that you want atomic append writes to the operating system when the OS file handles are created.
This is done by specifying O_APPEND under Posix(Linux,Unix), and FILE_APPEND_DATA under Windows.
In C# you don't call the OS 'open', or 'CreateFile' system calls directly, but there are ways to get this result.
I asked how to do this under Windows a while ago, and got two good answers here: How can I do an atomic write/append in C#, or how do I get files opened with the FILE_APPEND_DATA flag?
Basically, you can use FileStream() or PInvoke, I would suggest FileStream() over PInvoke for obvious reasons.
You can use constructor arguments to FileStream() to specify asynchronous file I/O in addition to the FileSystemRights.AppendData flag, which should give you both async I/O and atomic append writes to a file.
Warning: Some OSes have limits on the maximum number of bytes that can be atomically written this way, and exceeding that threshold will remove the OS promise of atomicity.
Because of this last gotcha, I would recommend staying with lock() style contention management when trying to address your problem within a single process.