Совместное использование класса между потоками - PullRequest
1 голос
/ 25 февраля 2020

У меня есть класс с именем "Vector", который по умолчанию содержит 10.000 элементов, которые всегда должны иметь одно и то же значение. Этот класс проверен и работает. Поэтому я использую метод setAndTest () из класса, чтобы установить значение всех элементов, которое затем немедленно проверяет, является ли объект Vector согласованным (что все элементы вектора имеют одинаковое значение).

В новом файле «main. cpp», я создал две функции: writer() и main(). writer() создает пользовательское количество потоков писателей (между 1 и 100), каждый из которых имеет свой уникальный идентификатор. Каждый писатель устанавливает и проверяет общий объект Vector для его идентификатора каждую секунду. Если писатель обнаруживает несоответствие в общем объекте Vector, setAndTest () возвращает false и должно быть напечатано следующее сообщение об ошибке: Error with thread #id Однако в 99% случаев он печатает Success with thread #id, тогда как я ожидал, что будет больше вариаций между ними.

Заголовки включены в основной файл. cpp:

#include <iostream>
#include "Vector.hpp"
#include <pthread.h>
#include <unistd.h>
using namespace std;

Объект Vector и writer ():

Vector VecObj;  //The Vector object (Defined in global scope)

void* writer(void *threadid)
{
    int threadid_ = *(int *)(threadid); 

    if(VecObj.setAndTest(threadid_))
    {
        std::cout << "\nSuccess with thread " << threadid_ << endl;
    }else
    {
        std::cout << "\nError with thread " << threadid_ << endl;
    }

    return NULL;
}

основная функция:

int main()
{
    start:
    int numOfThreads = 1;
    std::cout << "Enter amount of threads (must be between 1 & 100): ";
    std::cin >> numOfThreads;
    if(0 < numOfThreads && numOfThreads <= 100){
        std::cout << "You entered " << numOfThreads << " threads" << endl;
    }else{
        std::cout << "Amount of threads must be between 1 & 100" << endl;
        goto start;
    }

    pthread_t threadcreator[numOfThreads];

    for(int i = 0; i < numOfThreads; i++){
        pthread_create(&threadcreator[i], NULL, writer, &i);
        sleep(1);
    }

    for(int i = 0; i < numOfThreads; i++){
        pthread_join(threadcreator[i], NULL);
    }

}

Vector Class (Vector.hpp):

#ifndef VECTOR_HPP_
#define VECTOR_HPP_
#include <pthread.h>
using namespace std;

//=======================================================
// Class: Vector
// contains a size_-size vector of integers.
// Use the function setAndTest to set all elements
// of the vector to a certain value and then test that
// the value is indeed correctly set
//=======================================================
class Vector
{
public:
   Vector(unsigned int size = 10000) : size_(size)
      {
         vector_ = new int[size_];
         set(0);
      }

   ~Vector()
      {
         delete[] vector_;
      }

   bool setAndTest(int n)
      {
         set(n);
         return test(n);
      }

private:
   void set(int n)
      {
         for(unsigned int i=0; i<size_; i++) vector_[i] = n;
      }

   bool test(int n)
      {
         for(unsigned int i=0; i<size_; i++) if(vector_[i] != n) return false;
         return true;
      }

   int*           vector_;
   unsigned int   size_;

};

#endif

1 Ответ

2 голосов
/ 25 февраля 2020

Вы передаете каждому потоку указатель на одну и ту же переменную int. Эта переменная меняет значение на каждой итерации l oop. writer() ожидает получить то же значение int, которое было присвоено pthread_create(), но это не гарантируется в вашем коде, даже при вызове sleep().

Для передачи int правильно, передайте фактическое значение int, а не указатель на int, например:

#include <iostream>
#include <vector>
#include <cstdint>
#include <pthread.h>
#include "Vector.hpp"

Vector VecObj;

void* writer(void *arg)
{
    int threadid_ = static_cast<int>(reinterpret_cast<intptr_t>(arg));

    if (VecObj.setAndTest(threadid_))
    {
        std::cout << "\nSuccess with thread " << threadid_ << std::endl;
    }
    else
    {
        std::cout << "\nError with thread " << threadid_ << std::endl;
    }

    return NULL;
}

int main()
{
    int numOfThreads = 0;

    do {
        std::cout << "Enter amount of threads (must be between 1 & 100): ";
        std::cin >> numOfThreads;
        if (0 < numOfThreads && numOfThreads <= 100){
            std::cout << "You entered " << numOfThreads << " threads" << std::endl;
            break;
        }
        std::cout << "Amount of threads must be between 1 & 100" << std::endl;
    }
    while (true);

    std::vector<pthread_t> threadcreator(numOfThreads);

    for(int i = 0; i < numOfThreads; i++){
        pthread_create(&threadcreator[i], NULL, writer, reinterpret_cast<void*>(i));
    }

    for(int i = 0; i < numOfThreads; i++){
        pthread_join(threadcreator[i], NULL);
    }

    return 0;
}

Если вы действительно хотите использовать int* указатели, вам придется выделить отдельный int для каждого потока, например:

#include <iostream>
#include <vector>
#include <pthread.h>
#include "Vector.hpp"

Vector VecObj;

void* writer(void *arg)
{
    int threadid_ = *static_cast<int*>(arg);

    if (VecObj.setAndTest(threadid_))
    {
        std::cout << "\nSuccess with thread " << threadid_ << std::endl;
    }
    else
    {
        std::cout << "\nError with thread " << threadid_ << std::endl;
    }

    return NULL;
}

int main()
{
    int numOfThreads = 0;

    do {
        std::cout << "Enter amount of threads (must be between 1 & 100): ";
        std::cin >> numOfThreads;
        if (0 < numOfThreads && numOfThreads <= 100){
            std::cout << "You entered " << numOfThreads << " threads" << std::endl;
            break;
        }
        std::cout << "Amount of threads must be between 1 & 100" << std::endl;
    }
    while (true);

    std::vector<pthread_t> threadcreator(numOfThreads);
    std::vector<int> threadids(numOfThreads);

    for(int i = 0; i < numOfThreads; i++){
        threadids[i] = i;
        pthread_create(&threadcreator[i], NULL, writer, &threadids[i]);
    }

    for(int i = 0; i < numOfThreads; i++){
        pthread_join(threadcreator[i], NULL);
    }

    return 0;
}

Или, если вы действительно хотите передать указатель int* на один int, используйте std::conditional_variable или другой ожидаемый сигнал, чтобы убедиться, что каждый поток фактически захватил значение int, прежде чем позволить l oop изменить его значение, например:

#include <iostream>
#include <vector>
#include <conditional_variable>
#include <mutex>
#include "Vector.hpp"
#include <pthread.h>

Vector VecObj;
std::condition_variable cv;
std::mutex cv_m;
bool captured = false;

void* writer(void *arg)
{
    int threadid_;

    {
    std::lock_guard<std::mutex> lk(cv_m);
    threadid_ = *static_cast<int*>(arg);
    captured = true;
    }
    cv.notify_one();

    if (VecObj.setAndTest(threadid_))
    {
        std::cout << "\nSuccess with thread " << threadid_ << std::endl;
    }
    else
    {
        std::cout << "\nError with thread " << threadid_ << std::endl;
    }

    return NULL;
}

int main()
{
    int numOfThreads = 0;

    do {
        std::cout << "Enter amount of threads (must be between 1 & 100): ";
        std::cin >> numOfThreads;
        if (0 < numOfThreads && numOfThreads <= 100){
            std::cout << "You entered " << numOfThreads << " threads" << std::endl;
            break;
        }
        std::cout << "Amount of threads must be between 1 & 100" << std::endl;
    }
    while (true);

    std::vector<pthread_t> threadcreator(numOfThreads);

    for(int i = 0; i < numOfThreads; i++){
        std::unique_lock<std::mutex> lk(cv_m);
        captured = false;
        pthread_create(&threadcreator[i], NULL, writer, &i);
        cv.wait(lk, [](){ return captured; });
    }

    for(int i = 0; i < numOfThreads; i++){
        pthread_join(threadcreator[i], NULL);
    }

    return 0;
}

UPDATE : о, теперь я вижу еще одну серьезную проблему , У вас есть несколько потоков, которые записывают и читают из одного Vector объекта в памяти без синхронизации. Это не безопасно делать. В то время как один поток читает из элемента в массиве Vector, другой поток может записывать новое значение в этот же элемент, и нет никакой гарантии, что этот элемент останется согласованным в обеих операциях. Вы ДОЛЖНЫ синхронизировать доступ к объекту Vector, поскольку он используется несколькими потоками, например:

...
#include <mutex>
...

Vector VecObj;
std::mutex vec_m;
...

void* writer(void *threadid)
{
    int threadid_ = ...;
    bool testResult;

    {
    std::lock_guard lk(vec_m);
    testResult = VecObj.setAndTest(threadid_);
    }

    if (testResult)
    {
        std::cout << "\nSuccess with thread " << threadid_ << std::endl;
    }
    else
    {
        std::cout << "\nError with thread " << threadid_ << std::endl;
    }

    return NULL;
}

...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...