Почему моя многопоточная программа не завершается должным образом?

#c #multithreading

#c #многопоточность

Вопрос:

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

Чего мне здесь не хватает? Я предполагаю, что это какая-то уловка для завершения потоков, но, насколько я могу судить, у меня это покрыто pthread_exit . Пожалуйста, взгляните и дайте мне знать, если вам что-то бросится в глаза. Спасибо!

 #include <iostream>
#include <time.h>
#include <vector>
#include <fstream>
#include <sstream>
#include <cstdlib>
#include <pthread.h>
#include <string>

using namespace std;

struct threadData {
    int     thread_id;
    int*    tArray;
    int     tArraySize;
    int     tQuery;
};

//this is included because for whatever reason the std::to_string fxn is not recognized by my compiler
template <typename T>
std::string to_string(T value)
{
      //create an output string stream
      std::ostringstream os ;

      //throw the value into the string stream
      os << value ;

      //convert the string stream into a string and return
      return os.str() ;
}

//Counts the number of times the query is in the array
int countInstance(int* array, int arraySize, int query)
{
    int numInstance = 0;

    for ( int i = 0; i < arraySize; i  ) 
    {
        if (array[i] == query) 
            numInstance  = 1;
    }

    return numInstance;
}

void *threadFunction(void *threadArg)
{
    struct threadData *thisThread;

    thisThread = (struct threadData *) threadArg;

    //Originally wanted to use this to print each string, but it seemed to have issues printing correctly due to simultaneous threads
    /*cout << "Query: "         << thisThread->tQuery
         << "tCount:  "    << countInstance(thisThread->tArray, thisThread->tArraySize, thisThread->tQuery) 
         << "tThreadID: "  << thisThread->thread_id << endl;*/

    //Decided to create a concatenated string of the desired phrase instead.
    string queryString = to_string(thisThread->tQuery);
    string instanceString = to_string(countInstance(thisThread->tArray, thisThread->tArraySize, thisThread->tQuery));
    string idString = to_string(thisThread->thread_id);
    string outputString = "Query: "   queryString   "tCount:  "   instanceString   "tThreadID: "   idString   "n";

    cout << outputString;

    pthread_exit(NULL);
}

int main(void)
{   
    int arraySize = 1000;
    int numArray[arraySize];

    srand(time(NULL));

    //Populate array with random values ranged [0, 100]
    for ( int i = 0; i < arraySize; i  ) 
    {
        numArray[i] = (rand() % 101);
    }

    vector<int> numList;

    numList.push_back(1);
    numList.push_back(3);
    numList.push_back(5);
    numList.push_back(7);

    pthread_t threads[numList.size()];
    struct threadData data[numList.size()];
    int rc;

    for (int i = 0; i < numList.size(); i  )
        {
            data[i].tArray = numArray;
            data[i].tArraySize = arraySize;
            data[i].thread_id = i;
            data[i].tQuery = numList[i];
            rc = pthread_create(amp;threads[i], NULL, threadFunction, (void *)amp;data[i]);
            if (rc) 
                exit(-1);
        }
    pthread_exit(NULL);
    return 0;
}
  

Комментарии:

1. Указатель threadArg , который вы передаете ThreadFunction(), является ненужным. Это локальная переменная функции, которая может больше не выполняться к моменту запуска потока. Найдите более постоянное место для хранения данных. Типичная ошибка с запуском и забыванием потоков, кстати.

2. @HansPassant, какое решение было бы рекомендовано здесь? Разве я не должен размещать данные в этой структуре?

Ответ №1:

Вместо вызова pthread_exit(NULL) вы должны поместить цикл join вызовов в ожидание завершения потоков:

 for (int i=0; i<numlist.size(); i  ) {
    void *rv;
    pthread_join(threads[i], amp;rv);
}
  

pthread_exit используется в потоке, который отличается от основного потока, чтобы прервать его до его естественного завершения.

Комментарии:

1. Спасибо! Это сработало отлично. Знаете ли вы, в чем практическая разница между запуском этого внутри исходного цикла и последующим запуском? Я заметил, что при запуске after в виде отдельного цикла потоки завершаются в основном по порядку, но не обязательно всегда. Однако, когда я вставил это в конец существующего цикла, кажется, что потоки печатаются в том порядке, в котором они были созданы.

2. join Операция означает «дождитесь завершения потока». Если вы поместите join внутрь цикла, то ваша программа на самом деле вообще не будет многопоточной (основной поток создаст новый поток, но затем немедленно остановится, ожидая его завершения, прежде чем запускать следующий). Если вы сначала запустите все потоки, а затем дождетесь их всех позже, то они будут выполняться параллельно. Конечно, чтобы увидеть параллелизм, работа должна быть достаточно долгой, а для увеличения скорости вам также нужно многопоточное оборудование: на одноядерном процессоре многопоточные алгоритмы просто займут больше времени.