Как сделать потоки функций безопасными в С++?

Скажем, у меня есть пул потоков, у которого есть 5 дочерних потоков. И они вызывают функцию под названием "functionA()". Как сделать функцию безопасной потоком?

Также, если эти 5 потоков вызывается одновременно, они выполняются одновременно?  или они ждут, пока поток, который в настоящее время работает в функции, будет завершен?

Спасибо заранее.

Ответ 1

Функция уже потокобезопасна, если она не изменяет нелокальную память и не вызывает никакой функции. В этом (тривиальном) случае вам ничего не нужно делать.

Вы действительно хотите думать о защите данных, а не о функциях. Например, скажем, что функция изменяет нелокальную структуру данных X. Предоставляет мьютекс для защиты X и блокировки его перед каждым доступом и разблокировать его после. У вас может быть больше, чем une-функция, которая обращается к X (например, insertX(), deleteX(),...). Пока вы защищаете данные, все будет в порядке.

Ответ 2

Вам нужно убедиться, что функция работает одинаково каждый раз, когда она вызывается.

Это обычно означает, что вам необходимо защитить любые данные, которые доступны/записаны в функции.

Для автономных бесплатных функций, которые обычно означают удаление статических элементов из функции.

Если вы ссылаетесь на функцию-член какого-либо класса, то вам нужно быть более осторожным - поскольку класс обычно будет хранить данные независимо от функции-члена, которая может меняться между вызовами этого элемента. Обычно вы делаете это с помощью мьютекса.

Например:

//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;
  }
}

Это можно устранить, добавив мьютекс, который требует, чтобы два потока не могли одновременно запускать элемент запуска. См. Boost.Thread для хорошего примера мьютекса.

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;
  }
}

Ответ 3

Используя мьютекс, вы можете это сделать.

или:

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

или внутри функции A();

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

осторожно со вторым решением, потому что если у функции есть другие пути выхода (например, возврат в середине), а мьютекс не разблокирован, у вас есть ситуация, называемая тупиком.

Ответ 4

Один из способов сделать это - использовать мьютекс, чтобы гарантировать, что только один поток выполняет функцию в любой момент времени. Конечно, это предполагает, что это тип безопасности потоков, о котором вы говорите. Быть потокобезопасным может означать много разных вещей. Например, прочитайте сообщение Эрика Липперса под названием Что вы называете "потокобезопасным" ?

Если эти 5 потоков вызывается одновременно, они выполняются одновременно или они ждут, пока поток, который в настоящее время работает в функции, будет завершен?

Если вы не навязываете какой-либо механизм синхронизации (например, mutex), потоки выполняют эту функцию одновременно. Независимо от того, является ли это проблемой для вашей функции и что такое решение, зависит от шаблонов доступа к данным этой функции.

Ответ 5

Это зависит от того, что вы хотите сделать/что вы понимаете под потокобезопасностью. Обычно вы не хотите, чтобы потоки одновременно обрабатывали одни и те же данные, чтобы вы сериализовали это. Но это означает, что если один поток находится в функции, все остальные должны ждать. Это называется мьютексом (взаимное исключение). Другим способом было бы иметь несколько потоков в функции, но ограниченное число. Там вы можете применить семафор.

Итак, что вы хотите сделать?