#c #class #locking #pthreads #mutex
#c #класс #блокировка #pthreads #мьютекс
Вопрос:
Я много часов искал решение, но не могу найти простой ответ. Я получил класс, который использует pthreads. Фактический указатель на функцию статичен внутри класса, и мне нужно заблокировать мьютекс, потому что пока я получаю «странные» результаты (параметры передаются неправильно).
Однако pthread_mutex_lock и unlock не будут работать в функции, предоставленной потоку, потому что она находится в статической функции-члене, но я не могу сделать функцию нестатической, потому что она не будет работать внутри класса, и я не могу переместить ее за пределы класса, потому что она не сможет получить доступ к требуемой информации.
Следующий код должен объяснить:
class Fight{
pthread_mutex_t thread_mutex;
static void *thread_run_fighter(void *temp);
public:
Fight();
bool thread_round(Individual amp;a, int a_u, Individual amp;b, int b_u);
std::vector<Individual> tournament();
};
И cpp-файл:
Fight::Fight(){
thread_mutex = PTHREAD_MUTEX_INITIALIZER;
}
bool Fight::thread_round(Individual amp;a, int a_u, Individual amp;b, int b_u){
if (a.saved and b.saved){
a.uniform = a_u;
b.uniform = b_u;
Individual *one = amp;a;
Individual *two = amp;b;
pthread_t a_thread, b_thread;
int a_thread_id, b_thread_id;
a_thread_id = pthread_create(amp;a_thread,NULL,Fight::thread_run_fighter,(void*) one);
b_thread_id = pthread_create(amp;b_thread,NULL,Fight::thread_run_fighter,(void*) two);
pthread_join( a_thread, NULL);
pthread_join( b_thread, NULL);
return true;
}
else{
return false;
}
}
void *Fight::thread_run_fighter(void *temp){
Individual *indiv;
indiv = (class Individual*)temp;
pthread_mutex_lock( amp;thread_mutex );
indiv->execute(indiv->uniform);
pthread_mutex_unlock( amp;thread_mutex );
}
Я был бы очень признателен, если бы кто-нибудь мог пролить некоторый свет на это. Я застрял на несколько часов, и я не мог найти никакой информации вообще.
Спасибо!
Ответ №1:
Под «не работает», я полагаю, вы имеете в виду, что он не будет компилироваться, поскольку вы пытаетесь использовать элемент экземпляра в static
функции-члене.
Но главный вопрос в том, почему вы пытаетесь использовать потоки для этого?
Функция потока, которая у вас есть, в любом случае полностью защищена мьютексом — вы получите ту же (или лучшую) производительность, просто вызвав
a.execute(a.uniform);
b.execute(b.uniform);
вместо того, чтобы запускать потоки, а затем ждать их завершения.
Но если вы действительно хотите использовать потоки (возможно, вы изучаете их) и хотите, чтобы ваша статическая функция-член могла работать с членами экземпляра, вот несколько указаний. Чтобы заставить это работать, вам нужно каким-то образом передать экземпляр объекта Fight в static
функцию thread:
// somewhere in `class Fight` definition:
//
// a structure that will let you pass a Fight* instance pointer
// along with an Individual* to work on in the the
// thread function
struct context {
Fight* me;
Individual* indiv;
};
// ...
// in Fight::thread_round():
// package up data to pass to the thread function
context one = {this, amp;a }; // instead of Individual *one = amp;a;
context two = {this, amp;b }; // instead of Individual *two = amp;b;
Наконец, Fight::thread_run_fighter()
:
void *Fight::thread_run_fighter(void *temp)
{
// pull out the Fight object instance and the Individual
// object to work on
context* ctx = (context*) temp;
Individual *indiv = ctx->indiv;
Fight* me = ctx->me;
// do the work (in a pretty serialized fashion, unfortunately)
pthread_mutex_lock( amp;me->thread_mutex );
indiv->execute(indiv->uniform);
pthread_mutex_unlock( amp;me->thread_mutex );
return 0;
}
Комментарии:
1. Под «проблемами» я подразумеваю, что я получаю непредсказуемые ошибки, такие как отдельные элементы с измененными параметрами. Я не упоминал об этом в сообщении, но мне нужно выполнить поток, потому что фактическое выполнение прерывается, а затем ожидает. Но я думаю, вы нашли проблему, это не имеет отношения к мьютексам, это потому, что я установил его статическим, он продолжает выполнять первый отдельный параметр, переданный как param, и работает только тогда, когда объект воссоздан и функция отозвана! Я попробую ваше предложение и посмотрю, смогу ли я заставить его работать! Спасибо!
2. @Alex: Думаю, я не уверен, как вы доберетесь до проблем во время выполнения, поскольку я не могу понять, как
thread_run_fighter()
представленное в вопросе вообще будет компилироваться. Поскольку это статическая функция-член, пытающаяся использовать нестатические члены, это должно вызвать ошибку во время компиляции. Но если вы каким-то образом разблокированы, это хорошая новость.3. Ну, я попробовал то, что вы предложили, но я все еще получаю «странные» проблемы. Параметры передаются неправильно, отдельные пользователи утверждают, что они не были сохранены и т.д. Я думаю, что я иду по неправильному пути. Однако спасибо вам за то, что показали мне мою ошибку и как использовать блокировку и разблокировку мьютекса! PS: Нет, я не получал никаких ошибок компиляции!
Ответ №2:
Первый вопрос, который я хотел бы задать: нужен ли вам переносимый код? Если да, никогда не передавайте функцию C в pthread_create. Причина: интерфейс для pthread_create требует, чтобы функция была объявлена как extern «C», и вам повезло (благодаря x86 🙂 ), что для этого подходит статический метод-член, но нет гарантии, что то же самое будет на других платформах или компиляторах.
Второе, которое вы вызвали thread_mutex = PTHREAD_MUTEX_INITIALIZER;
после создания мьютекса, насколько я помню, в стандарте сказано, что это разрешено только при инициализации.
И, наконец, pthread_mutex_lock( amp;thread_mutex )
использование статического метода не будет разрешено, потому что статический метод не имеет доступа к нестатическим членам объекта, поэтому вам нужно передать указатель на ваш объект. Вы можете объявить pthread_mutex_t thread_mutex;
и void *thread_run_fighter(void *temp);
как глобальные, как я вижу, это будет самый простой способ в данном случае.
И некоторые примечания: как насчет boost::threads? Я думаю, что будет лучше использовать его вместо создания собственного решения…
Ответ №3:
Я думаю, все, что вы пропустили, это использовать amp;indiv->thread_mutex
вместо того, чтобы опускать indiv->
объект.
РЕДАКТИРОВАТЬ: Обратите внимание, что, вероятно, лучше использовать статическое приведение, а не приведение в стиле C «shotgun»: Individual *indiv = static_cast<Individual*>(temp);
Ответ №4:
внутри статического thread_run_fighter
вы используете нестатический thread_mutex
. thread_mutex должен быть создан с помощью pthread_mutex_init перед его использованием.
Комментарии:
1. На самом деле
thread_mutex = PTHREAD_MUTEX_INITIALIZER;
это заменаpthread_mutex_init
вызова, когда все, что вам нужно, — это прямые значения по умолчанию.2. да, но это все еще не статично. и ctor может запускаться, а может и не запускаться до вызова thread_run_fighter… Я думаю, что код не компилируется…
3. ах, извините, кофе пока нет… зачем вы создаете потоки? теперь оба боя блокируются на одном и том же мьютексе, поэтому выполняется indiv-> execute для a и выполняется indiv-> execute для b, поскольку мьютекс тот же. вы могли бы также вызвать две функции в thread_round, поскольку он в любом случае ожидает их. чего вы на самом деле хотите достичь с помощью mutices?
4.
PTHREAD_MUTEX_INITIALIZER
может использоваться только в качестве инициализатора статически выделенных мьютексов (то есть глобальных переменных,static
членов класса илиstatic
членов функции). Его нельзя использовать в качестве динамического инициализатора, как в этом случае, и это является неопределенным поведением. В этом случае вам нужно инициализировать его с помощьюpthread_mutex_init
.5. Я изменил конструктор, чтобы использовать pthread_mutex_init, не знал, что есть разница. Возможно, я пошел по неверному пути. Вероятно, мне следует реализовать мьютекс внутри отдельного класса или вектора, который содержит отдельных пользователей, а не класс fight. Я создаю потоки для распараллеливания выполнения, это приложение для оптимизации генетического программирования. Каждый отдельный файл-> execute() разветвляется и запускает другой двоичный файл, который подключается к серверу моделирования, пока приложение ожидает его завершения. Я хочу запустить 4 или 8 потоков вместо одного, чтобы ускорить вычисления.