23

Let's say I have a thread pool that has 5 child threads. And they are calling a function called "functionA()" How do I make the function to be thread safe?

Also if those 5 threads are called at the same time then are they executed concurrently? or do they wait until a thread that currently works in the function to be finished ?

Thanks in advance..

razlebe
  • 7,134
  • 6
  • 42
  • 57
codereviewanskquestions
  • 13,460
  • 29
  • 98
  • 167
  • 3
    Depends on what the function does and what shared resources it uses – Armen Tsirunyan Jun 20 '11 at 12:14
  • 1
    that depends on what the function is doing - if it accesses *shared state* then you need a synchronization mechanism (which depends on OS), if the function doesn't access shared state, then you don't need to worry... – Nim Jun 20 '11 at 12:15
  • 2
    Functions named `functionA()` are never thread-safe. You need to change the name to `functionZ()` `:-p` Seriously, you expect us to tell you how to make code threadsafe based on nothing more than a fake function name? – Ben Voigt Jun 20 '11 at 12:17
  • 5
    +1 I do think the general question of; "how do you make a function thread safe" is a valid question. Without discussion the exact purpose or resources used by the function. – Bjarke Freund-Hansen Jun 20 '11 at 12:17

5 Answers5

22

A function is already thread safe if it doesn't modify non-local memory and it doesn't call any function that does. In this (trivial) case, you don't have to do anything.

You really want to think about protecting data, not functions. For example, let's say that the function modifies non-local data structure X. Provide a mutex to protect X and lock it before each access and unlock it after. You may have more than one function that accesses X (e.g. insertX(), deleteX(), ...). As long as you protect the data you'll be OK.

Richard Pennington
  • 19,673
  • 4
  • 43
  • 72
  • Is it correct to say that a function is thread safe even if it access memory that could be changed by another function simultaneously resulting a data race? See this [related question](https://stackoverflow.com/questions/67143880/what-is-the-definition-of-a-thread-safe-class-member-function-according-to-the-c). – m7913d Apr 17 '21 at 23:37
  • 1
    @m7913d, no this is actually not allowed to access that memory that way, some inconsistencies could occured on caller side when using that function. – Manifest Man Apr 18 '21 at 19:07
5

Using a mutex you can do that.

either:

mutex_lock(&mutex);
functionA();
mutex_unlock(&mutex);

or inside functionA();

int functionA() {
mutex_lock(&mutex);
// code
mutex_unlock(&mutex);
}

careful with the second solution, because if the function has other exit paths (a return in the middle for example) and the mutex in not unlocked, you have a situation called deadlock.

Vinicius Kamakura
  • 7,665
  • 1
  • 29
  • 43
  • 8
    That's why you use an RAII guard to call unlock no matter how the function ends. (BTW, the first version is equally broken, the mutex will be leaked in case of an exception). – Ben Voigt Jun 20 '11 at 12:21
  • Also, see Jan's comment on David's answer. – Ben Voigt Jun 20 '11 at 12:22
  • Regarding the first version: you really don't do any synchronization if you don't assure that __every__ other thread calling functionA() use __the same mutex!__ If a thread do not use that mutex or do not use a mutex at all, then there is no synchronization at all! – AJed Apr 13 '14 at 01:06
4

You need to make sure that the function runs identically everytime it is called.

This generally means that you need to protect any data that is accessed/written in the function.

For stand-alone free functions that generally means removing static elements from function.

If you are referring to a member function of some class, then you need to be more careful - because the class will generally store data independantely of the member function which may change between calls to that member. You will generally do this with a mutex.

For example:

//Threadsafe because the function do not change each time it is called
double add_one(double d)
{
  return d+1;
}

struct A
{
  double m_data;

  //Not thread safe because if function called simultaneously then m_data could change more than once before it is returned
  double 
  run(double d)
  {
    m_data += d;
    return m_data/2.0;
  }
}

This can be fixed by adding a mutex that demands that two threads cannot run the run member simultaneously. See Boost.Thread for a good mutex example.

struct A
{
  double m_data;
  mutex m_mutex;

  //Thread safe because mutex protects m_data from being written by multiple threads at once
  double 
  run(double d)
  {
    lock(m_mutex);  //lock the function, unlock on destruction.
    m_data += d;
    return m_data/2.0;
  }
}
Tom
  • 5,219
  • 2
  • 29
  • 45
  • 3
    [`std::lock`](https://en.cppreference.com/w/cpp/thread/lock) does not automatically unlock, you probably want to use [`std::lock_guard`](https://en.cppreference.com/w/cpp/thread/lock_guard) – m7913d Apr 21 '21 at 18:35
  • Please include an example to clarify that a const (read-only) function also needs mutex protection if another function may write to the data member at the same time. – m7913d Apr 21 '21 at 18:38
2

One way to do this is to use a mutex to ensure that only a single thread is executing in the function at any one time. Of course, this presumes that this is the type of thread safety you are referring to. Being thread-safe could mean many different things. For example, take a read of Eric Lipperts post titled What is this thing you call "thread safe"?

If those 5 threads are called at the same time then are they executed concurrently or do they wait until a thread that currently works in the function to be finished?

Unless you impose some synchronisation mechanism (e.g. a mutex) then the threads execute the function concurrently. Whether or not this is a problem for your function, and what the solution is, depends on the data access patterns of that function.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • EH? Why is there a down vote on this answer? – Nim Jun 20 '11 at 12:16
  • 5
    Never ever suggest to "just use a mutex". They protect *data*, not functions and analysis of the data used is in order before suggesting anything. – Jan Hudec Jun 20 '11 at 12:17
0

It depends on what you want to do/ what you understand under threadsafe. Normally you dont want threads to access the same data concurrently so you serialize this. But this means if one thread is in the function all others have to wait. This is called a mutex(mutual exclusion). The other way would be to have multiple threads in the function but a limited number. There you could apply a semaphore.

So what do you want to do?