Как безопасно работать с параметрами в потоках, используя C ++ и Pthreads? - PullRequest
2 голосов
/ 09 июня 2010

У меня возникли некоторые проблемы с программой, использующей pthreads, где происходят случайные сбои, которые могут быть связаны с тем, как потоки работают с данными

Поэтому у меня есть несколько основных вопросов о том, как программировать с использованием потоков,и расположение памяти:

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

std::string SomeClass::somefunc(const std::string &strOne, const std::string &strTwo)
{
 //Error checking of strings have been omitted
 std::string result = strOne.substr(0,5) + strTwo.substr(0,5);
 return result;
}
  1. Правильно ли предположить, что строки, будучи динамическими, хранятся в куче, но ссылка на строку размещается встек во время выполнения?

Стек: адрес указателя [Some mem addr], на котором находится строка в куче

Heap: память [Some mem addr], выделенная дляисходная строка, которая может увеличиваться или уменьшаться

Чтобы сделать поток функции безопасным, функция расширяется следующим мьютексом (который объявлен как закрытый в блокировке SomeClass):

std::string SomeClass::somefunc(const std::string &strOne, const std::string &strTwo)
{
 pthread_mutex_lock(&someclasslock);

 //Error checking of strings have been omitted
 std::string result = strOne.substr(0,5) + strTwo.substr(0,5);

 pthread_mutex_unlock(&someclasslock); 

 return result;
}

Является ли это безопасным способом блокировки операций, выполняемых над строками (все три), или может ли планировщик остановить поток в следующих случаях, которые, как я предполагаю, могут испортить предполагаемыйлогика:

а.Сразу после вызова функции и установки параметров: strOne & strTwo в двух ссылочных указателях, которые функция имеет в стеке, планировщик отнимает время обработки для потока и пропускает новый поток, который перезаписывает ссылкууказатели на функцию, которая затем снова останавливается планировщиком, позволяя первому потоку вернуться обратно?

b.Может произойти то же самое со строкой «result»: первая строка создает результат, разблокирует мьютекс, но перед возвратом планировщик запускает другой поток, который выполняет всю свою работу, перезаписывая результат и т. Д.

Или опорные параметры / строка результата помещаются в стек, пока другой поток выполняет свою задачу?

Это безопасный / правильный способ сделать это в потоках и «вернуть» результат, чтобы передать ссылку на строку, которая вместо этого будет заполнена результатом:

void SomeClass:: somefunc (const std :: string & strOne, const std :: string & strTwo, std :: string result) {pthread_mutex_lock (& ​​someclasslock);

// Проверка ошибок строк была опущена result = strOne.substr (0,5) + strTwo.substr (0,5);

pthread_mutex_unlock (& ​​someclasslock);}

Предполагаемая логика заключается в том, что несколько объектов класса SomeClass создают новые потоки и передают сами объекты в качестве параметров, а затем вызывают функцию: "someFunc":

int SomeClass::startNewThread()
{

 pthread_attr_t attr;
 pthread_t pThreadID;

 if(pthread_attr_init(&attr) != 0)
  return -1;

 if(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0)
  return -2;

 if(pthread_create(&pThreadID, &attr, proxyThreadFunc, this) != 0)
  return -3;

 if(pthread_attr_destroy(&attr) != 0)
  return -4;

 return 0;
}

void* proxyThreadFunc(void* someClassObjPtr)
{
 return static_cast<SomeClass*> (someClassObjPtr)->somefunc("long string","long string");
}

Извините за длинное описание.Но я надеюсь, что вопросы и предполагаемая цель ясны, если не сообщите мне, и я уточню.

С наилучшими пожеланиями.Chris

Ответы [ 2 ]

1 голос
/ 09 июня 2010

1 a / b: нет, никто не может случиться. Параметры функций и их возвращаемые значения расположены в стеке, и каждый поток имеет свой собственный стек. Однако, другие вещи могут пойти не так:

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

Я рекомендую вам создать новый объект SomeClass для каждого потока. В этом случае все члены этих объектов доступны только одному потоку и не нуждаются в защите от блокировки. Недостатком будет то, что вы не сможете получить к ним доступ из своего основного потока после запуска нового потока. Если это требуется, тогда вы должны защитить их с помощью блокировки (замок также будет членом этого класса).

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

0 голосов
/ 09 июня 2010

Общий совет: постарайтесь свести к минимуму места, где может происходить доступ к общим данным. Под общими данными я подразумеваю данные, к которым в любой момент может обратиться из любого потока.

Существует несколько общих подходов к многопоточному программированию:

Производитель потребитель

Читатель писателей

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

...