Pointers

Pointers in Multithreaded Code

What are some common pitfalls when working with pointers in multithreaded applications?

3D character art

Working with pointers in multithreaded applications can be tricky and introduce various issues if not handled carefully. Here are some common pitfalls and how to avoid them:

Race Conditions

Race conditions occur when multiple threads access shared data concurrently, and at least one thread modifies the data.

#include <iostream>
#include <thread>
#include <vector>

class SharedResource {
 public:
  int* data{nullptr};
};

void modifyData(SharedResource* resource) {
  if (resource->data == nullptr) {
    resource->data = new int(42);  
  }
  (*resource->data)++;  
}

int main() {
  SharedResource resource;
  std::vector<std::thread> threads;

  for (int i = 0; i < 10; ++i) {
    threads.emplace_back(modifyData, &resource);
  }

  for (auto& t : threads) {
    t.join();
  }

  std::cout << "Final value: "
    << *resource.data << "\n";
  delete resource.data;
}
Final value: 51

This code has multiple issues:

  • Multiple threads might try to initialize data simultaneously.
  • Incrementing resource->data is not atomic and can lead to lost updates.

To fix this, use proper synchronization mechanisms like mutexes:

#include <iostream>
#include <mutex>
#include <thread>
#include <vector>

class SharedResource {
 public:
  int* data{nullptr};
  std::mutex mutex;
};

void modifyData(SharedResource* resource) {
  std::lock_guard<std::mutex> lock(
    resource->mutex
  );
  if (resource->data == nullptr) {
    resource->data = new int(42);
  }
  (*resource->data)++;
}

int main() {
  SharedResource resource;
  std::vector<std::thread> threads;

  for (int i = 0; i < 10; ++i) {
    threads.emplace_back(modifyData, &resource);
  }

  for (auto& t : threads) {
    t.join();
  }

  std::cout << "Final value: "
    << *resource.data << "\n";
  delete resource.data;
}
Final value: 52

Dangling Pointers

Dangling pointers occur when a pointer refers to memory that has been deallocated. This is particularly dangerous in multithreaded contexts:

#include <iostream>
#include <thread>

class Resource {
 public:
  int value{42};
};

Resource* globalResource = nullptr;

void createResource() {
  globalResource = new Resource();
}

void useResource() {
  std::this_thread::sleep_for(
    std::chrono::milliseconds(10));
  if (globalResource) {
    std::cout << globalResource->value << "\n";  
  }
}

void deleteResource() {
  delete globalResource;
  globalResource = nullptr;
}

int main() {
  std::thread t1(createResource);
  std::thread t2(useResource);
  std::thread t3(deleteResource);

  t1.join();
  t2.join();
  t3.join();
}
42

This code might crash if deleteResource runs before useResource. To fix this, use proper synchronization and consider using smart pointers:

#include <iostream>
#include <memory>
#include <mutex>
#include <thread>

class Resource {
 public:
  int value{42};
};

std::shared_ptr<Resource> globalResource;
std::mutex resourceMutex;

void createResource() {
  std::lock_guard<std::mutex> lock(resourceMutex);
  globalResource = std::make_shared<Resource>();
}

void useResource() {
  std::this_thread::sleep_for(
    std::chrono::milliseconds(10));
  std::lock_guard<std::mutex> lock(resourceMutex);
  if (globalResource) {
    std::cout << globalResource->value << "\n";
  }
}

void deleteResource() {
  std::lock_guard<std::mutex> lock(resourceMutex);
  globalResource.reset();
}

int main() {
  std::thread t1(createResource);
  std::thread t2(useResource);
  std::thread t3(deleteResource);

  t1.join();
  t2.join();
  t3.join();
}

Memory Leaks

Memory leaks can occur if a thread responsible for freeing memory terminates unexpectedly:

#include <chrono>
#include <thread>

void leakyFunction() {
  int* data = new int[1000];
  std::this_thread::sleep_for(
    std::chrono::seconds(1));
  // Thread might be terminated before
  // reaching this point
  delete[] data;  
}

int main() {
  std::thread t(leakyFunction);
  std::this_thread::sleep_for(
    std::chrono::milliseconds(500));

  // Detaching the thread,
  // potentially causing a leak
  t.detach();
}

To prevent this, use RAII (Resource Acquisition Is Initialization) principles and smart pointers:

#include <chrono>
#include <memory>
#include <thread>

void safeFunction() {
  auto data = std::make_unique<int[]>(1000);
  std::this_thread::sleep_for(
    std::chrono::seconds(1));
  // Memory will be automatically freed when
  // the unique_ptr goes out of scope
}

int main() {
  std::thread t(safeFunction);
  std::this_thread::sleep_for(
    std::chrono::milliseconds(500));
  t.detach();
}

Conclusion

When working with pointers in multithreaded applications:

  • Use proper synchronization mechanisms (mutexes, atomic operations).
  • Prefer smart pointers over raw pointers.
  • Use RAII to manage resource lifetimes.
  • Be cautious with global or shared pointers.
  • Consider using thread-safe data structures from the standard library.

Remember, multithreading adds complexity to pointer management. Always design your multithreaded code carefully and consider thread safety at every step.

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

3D art showing a progammer setting up a development environment
Part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

This course includes:

  • 60 Lessons
  • Over 200 Quiz Questions
  • 95% 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.

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