Использование большой структуры в качестве параметра с std async вызывает ошибку

#c #asynchronous #struct

#c #асинхронный #структура

Вопрос:

У меня есть большая структура, которую необходимо обработать. Поэтому я использую std async для выполнения этого в другом потоке. Вот простой пример ниже:

 #include <iostream>
#include <future>

 
using namespace std;

struct teststruct{
    float thisvalue[32][32][64];
};

void foo(teststruct thisdummy) {
    this_thread::sleep_for(chrono::milliseconds(234));
    cout << "hello";
}

int main() {
    teststruct thisteststruct;
    foo(thisteststruct); // this does work
    auto futurevalue = async(launch::async, foo, thisteststruct); //this doesnt work

    return 0;
}
  

У меня есть структура, которая представляет собой просто большой 3D-массив с плавающей точкой. Когда я запускаю это, я получаю сообщение об ошибке с ошибкой шины: 10. Просто вызов foo без использования async работает, но с использованием std async это не работает. Каким-то образом это связано с размером структуры, поскольку, когда я создаю структуру

 struct teststruct{
        float thisvalue[32][32][63];
};
  

это волшебным образом работает. Я попытался разделить структуру наполовину, чтобы вместо массива 32x32x32 вместо массива 32x32x64 и использовать teststructs в качестве параметров, но это не работает. Как я уже говорил выше, я думаю, что это связано с размером структуры. Как я могу это исправить?

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

1. Я предполагаю, что потоку недостаточно выделенной памяти, т. Е. 256 КБ — это максимум для вашей системы. Для потока хранится несколько дополнительных данных, поэтому вы не можете использовать все 256 КБ.

2. Разве вы не можете просто передать адрес этой структуры?

3. async() Заставляет ли main ждать, пока он не завершится? Если нет, то main() можно завершить (и уничтожить thisteststruct ) до того, как метод async получит копию.

Ответ №1:

Вероятно, вы раздуваете стек.

sizeof(teststruct) есть 32*32*64*sizeof(float) . Предполагая 4-байтовое значение с плавающей запятой, это 256 КБ, которые вы пытаетесь передать по значению foo .

Хотя есть флаги, которые вы можете передать компилятору для увеличения размера стека, вы действительно хотите избежать передачи больших объектов по значению. Поскольку для этого требуется создание копии в памяти. Передайте эту большую структуру с помощью shared_pointer . Это позволит передавать его в основном потоке и в асинхронном потоке без использования какого-либо значительного объема стековой памяти. Это также быстрее, поскольку позволяет избежать большой копии этого объекта.

 #include <iostream>
#include <future>
#include <memory>

 
using namespace std;

struct teststruct{
    float thisvalue[32][32][64];
};


void foo(shared_ptr<teststruct> thisdummy) {
    this_thread::sleep_for(chrono::milliseconds(234));
    cout << "hello";
}

int main() {
    auto thisteststruct = make_shared<teststruct>();
    foo(thisteststruct);
    auto futurevalue = async(launch::async, foo, thisteststruct);

    return 0;
}
  

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

1. Спасибо за идею, но поскольку данные будут изменены во время работы в потоке, мне пришлось бы использовать мьютекс, что создает узкое место. Но я обнаружил, что мне не нужно использовать все данные, а только часть, которая решает проблему. Но тем не менее спасибо!

Ответ №2:

Превращение его в указатель или ссылку должно сработать.

(непроверенный)

 void foo(teststruct *thisdummy) {
    this_thread::sleep_for(chrono::milliseconds(234));
    cout << "hello";
}

int main() {
    teststruct thisteststruct;
    foo(amp;thisteststruct); // this does work
    auto futurevalue = async(launch::async, foo, amp;thisteststruct); //this doesnt work

    futurevalue.get();

    return 0;
}
  

О чем идет речь в приведенном ниже обсуждении, так это:

Если std::future, полученный из std::async, не перемещается или не привязывается к ссылке, деструктор std::future заблокирует в конце полного выражения до завершения асинхронной операции, по сути, делая код, такой как следующий синхронный:

Смотрите это

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

1. Что происходит, когда main returns и destructs thisteststruct во время foo выполнения async в другом потоке и, предположительно, все еще обращается к этому объекту. 🙂

2. @selbie почти то же самое, что и программа OP, она завершает поток и закрывает программу.

3. Вы не можете гарантировать, что функция async отменена и возвращена до удаления teststruct . В его конкретном случае его асинхронная функция ничего не делает, так что никакого вреда. Но в тот момент, когда он изменяет его, чтобы получить доступ к этому параметру указателя, все ставки отменяются.

4. @selbie Хотя это правда, ваш ответ также не касается этого, поэтому я не знаю, почему вы жалуетесь на это здесь…

5. Я хочу сказать, что небезопасно передавать объект стека по указателю на функцию, которая выполняется в другом потоке. Решение shared_ptr хорошо работает в этом случае.