Как использовать pthread_mutex и его функции внутри класса?

#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 потоков вместо одного, чтобы ускорить вычисления.