Безопасен ли оператор записи вектора [] для записи? - PullRequest
1 голос
/ 28 апреля 2020

Допустим, у меня есть следующий фрагмент кода.

// Some function decleration
void generateOutput(const MyObj1& in, MyObj2& out);

void doTask(const std::vector<MyObj1>& input, std::vector<MyObj2>& output) {

    output.resize(input.size());

    // Use OpenMP to run in parallel

#pragma omp parallel for
    for (size_t i = 0; i < input.size(); ++i) {
        generateOutput(input[i], output[i]);
    }

}

Является ли вышеуказанный код безопасным? Я в основном озабочен тем, чтобы писать на output[i] Нужна ли какая-то блокировка? Или это не нужно? Например:


// Some function prototype
void generateOutput(const MyObj1& in, MyObj2& out);

void doTask(const std::vector<MyObj1>& input, std::vector<MyObj2>& output) {

    output.resize(input.size());

    // Use OpenMP to run in parallel

#pragma omp parallel for
    for (size_t i = 0; i < input.size(); ++i) {
        MyObj2 tmpOutput;
        generateOutput(input[i], tmpOutput);
#pragma omp critical
        output[i] = std::move(tmpOutput);
    }

}

Я не беспокоюсь о части чтения. Как упомянуто в этом ответе, похоже, что чтение input[i] является потокобезопасным.

Ответы [ 3 ]

3 голосов
/ 28 апреля 2020

output[i] не записывает в output. Это просто звонок на std::vector<MyObj2>::operator[]. Возвращает безымянный MyObj2&, который затем используется для вызова generateOutput. В последнем случае происходит запись.

Я предполагаю, что generateOutput сам по себе потокобезопасен, а также MyObj2, поскольку у нас нет кода для этого. Таким образом, запись в MyObj2& внутри generateOutput также является потокобезопасной.

В результате все детали являются потокобезопасными.

1 голос
/ 28 апреля 2020

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

Поскольку вы используете простой Параллельно для l oop, в котором каждый элемент доступен ровно один раз, это безопасно.

1 голос
/ 28 апреля 2020

Чтобы не делать каких-либо предположений относительно реализации std :: vector, вы можете изменить свой код, как показано ниже, чтобы сделать его потокобезопасным (адреса указателей будут определяться в разных зонах в памяти и, следовательно, будут безопасны для потоков)

// Some function decleration
void generateOutput(const MyObj1& in, MyObj2 *out); // use raw data pointer for output

void doTask(const std::vector<MyObj1>& input, std::vector<MyObj2>& output) {

    output.resize(input.size());

    // Use OpenMP to run in parallel


    auto data = output.data() ;// pointer on vector underlying data outside of OMP threading

    #pragma omp parallel for
    for (size_t i = 0; i < input.size(); ++i) {
        generateOutput(input[i], &data[i]); // access to distinct data elements  ie addresses (indexed by i only in from each omp thred)
    }

}
...