Does File.AppendAllText manage collisions (i.e. multi-user concurrency)?

The key is this method:

private static Stream CreateFile(string path, bool append, bool checkHost)
{
    FileMode mode = append ? FileMode.Append : FileMode.Create;
    return new FileStream(path, mode, FileAccess.Write, FileShare.Read, 4096, FileOptions.SequentialScan, Path.GetFileName(path), false, false, checkHost);
}

It's opening with FileShare.Read, meaning that other threads or processes can open the file for reading, but no other process/thread can open it for writing.

You probably wouldn't want it to allow multiple concurrent writers. Consider writing two very large buffers. It's very likely that they would end up being interleaved.

So, yes ... if you have multiple threads that might be appending to that file, you need to synchronize access, probably with a lock.

Another option, depending on your application, would be to have a consumer thread that reads text from a queue and appends to the file. That way, only one thread has access to the file. Other threads put the messages on a queue that the writer thread services. This is pretty easy to do with BlockingCollection, but is probably overkill unless you're writing to the file on a continual basis (as in logging).


Only one will win for writing, and it will be the first, any subsequent attempts will fail until the write lock is released (i.e. the buffer is flushed and the file closed) - however, it could be open for reading simultaneously (permissions depending).

Read - Allows subsequent opening of the file for reading. If this flag is not specified, any request to open the file for reading (by this process or another process) will fail until the file is closed. However, even if this flag is specified, additional permissions might still be needed to access the file.


I knew the topic is old, but I found out that after read https://stackoverflow.com/a/18692934/3789481

By implement EventWaitHandle, I can easily prevent collision with File.AppendAllText

    EventWaitHandle waitHandle = new EventWaitHandle(true, EventResetMode.AutoReset, "SHARED_BY_ALL_PROCESSES");

    Task[] tasks = new Task[ThreadCount];
    for (int counter = 0; counter < ThreadCount; counter++)
    {
        var dividedList = ....
        tasks[counter] = await Task.Factory.StartNew(async () => await RunTask(counter, dividedList, waitHandle));
    }

And the RunTask write to file

    private static async Task RunTask(int threadNum, List<string> ids, EventWaitHandle waitHandle)
    {
        Console.WriteLine($"Start thread {threadNum}");

        foreach (var id in ids)
        {
            // start waiting
            waitHandle.WaitOne();
            File.AppendAllText(@".\Result.txt", text + Environment.NewLine);
            waitHandle.Set();
            // until release

            // ninja code
        }
        Console.WriteLine($"End thread {threadNum}");
    }

I have tested with 500 threads, it worked well !!

Tags:

C#

.Net

Io