Working with the File System

std::filesystem Thread Safety

Is std::filesystem safe to use in multithreaded contexts?

Abstract art representing computer programming

std::filesystem is generally thread-safe for individual operations, meaning that multiple threads can safely perform filesystem operations simultaneously.

However, the standard does not guarantee atomicity or synchronization for compound operations, such as checking for a file's existence and then creating it if it doesn't exist.

This can lead to race conditions in certain scenarios. Here’s an example demonstrating basic thread safety in std::filesystem:

#include <filesystem>
#include <fstream>
#include <iostream>
#include <mutex>
#include <thread>
namespace fs = std::filesystem;

std::mutex mtx;

void create_file(const fs::path &file_path) {
  std::lock_guard<std::mutex> lock(mtx);  
  std::ofstream file(file_path);
  if (file) {
    std::cout << "Created file: "
      << file_path.string() << '\n';
  } else {
    std::cerr << "Failed to create file: "
      << file_path.string() << '\n';
  }
}

int main() {
  fs::path file1{R"(c:\test\file1.txt)"};  
  fs::path file2{R"(c:\test\file2.txt)"};  

  std::thread t1(create_file, file1);
  std::thread t2(create_file, file2);

  t1.join();
  t2.join();
}
Created file: c:\test\file1.txt
Created file: c:\test\file2.txt

In this example:

  • We define file1 and file2 as the paths to the files we want to create.
  • We launch two threads, each calling the create_file function to create a file.
  • Both threads run simultaneously and safely create their respective files without interference.

While individual filesystem operations are thread-safe, be cautious with compound operations. For example, if you need to check for the existence of a file and create it if it doesn't exist, you should use proper synchronization mechanisms like mutexes to avoid race conditions:

#include <filesystem>
#include <fstream>
#include <iostream>
#include <mutex>
#include <thread>

namespace fs = std::filesystem;

std::mutex mtx;

void create_file_if_not_exists(
  const fs::path &file_path) {
  std::lock_guard<std::mutex> lock(mtx);  
  if (!fs::exists(file_path)) {           
    std::ofstream file(file_path);
    if (file) {
      std::cout << "Created file: "
        << file_path.string() << '\n';
    } else {
      std::cerr << "Failed to create file: "
        << file_path.string() << '\n';
    }
  } else {
    std::cout << "File already exists: "
      << file_path.string() << '\n';
  }
}

int main() {
  fs::path file{R"(c:\test\file.txt)"};  

  std::thread t1(create_file_if_not_exists, file);
  std::thread t2(create_file_if_not_exists, file);

  t1.join();
  t2.join();
}
Created file: c:\test\file.txt
File already exists: c:\test\file.txt

In this example:

  • We use a std::mutex to synchronize access to the file creation code.
  • The std::lock_guard ensures that only one thread can check for the file’s existence and create it at a time.

This synchronization prevents race conditions and ensures that the file is created only once, even if multiple threads attempt to create it simultaneously.

In summary, while std::filesystem operations are thread-safe, always use synchronization mechanisms for compound operations to ensure proper behavior in a multi-threaded environment.

Answers to questions are automatically generated and may not have been reviewed.

A computer programmer
Part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Free, unlimited access

This course includes:

  • 125 Lessons
  • 550+ Code Samples
  • 96% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Free, Unlimited Access

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Screenshot from Warhammer: Total War
Screenshot from Tomb Raider
Screenshot from Jedi: Fallen Order
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved