crossplatform.ru

Здравствуйте, гость ( Вход | Регистрация )

2 страниц V   1 2 >  
Ответить в данную темуНачать новую тему
> std::shared_ptr и лямбда функции
alexy
  опции профиля:
сообщение 22.10.2013, 10:21
Сообщение #1


Студент
*

Группа: Участник
Сообщений: 44
Регистрация: 4.8.2010
Пользователь №: 1931

Спасибо сказали: 0 раз(а)




Репутация:   0  


Встолкнулся с такой проблемой. мне нужно передать указатель на объект в несколько лямбда функций, которые будут вызваны позднее из разных потоков. сразу встал вопрос кто уничтожит объект. ну я подумал сначала что засуну его в std::shared_ptr и делов-то. выходит типа такого
std::shared_ptr<foo> foo_obj(new foo());
// дальше у меня в цикле, но смысл такой
post1([this,foo_obj](){/*using foo_obj*/});
post2([this,foo_obj](){/*using foo_obj*/});
post3([this,foo_obj](){/*using foo_obj*/});
//....


теперь я думаю - он вобще когда-нибудь уничтожиться? или будет храниться веки вечные..
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Iron Bug
  опции профиля:
сообщение 22.10.2013, 13:25
Сообщение #2


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


shared_ptr - нет. уничтожается unique_ptr (это бывший auto_ptr). но если он используется в разных потоках, то должен передаваться по значению.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
alexy
  опции профиля:
сообщение 23.10.2013, 18:22
Сообщение #3


Студент
*

Группа: Участник
Сообщений: 44
Регистрация: 4.8.2010
Пользователь №: 1931

Спасибо сказали: 0 раз(а)




Репутация:   0  


то есть вот так нужно делать?
std::unique_ptr<foo> foo_obj(new foo());
post1([this,&foo_obj](){/*using foo_obj*/});
post2([this,&foo_obj](){/*using foo_obj*/});
post3([this,&foo_obj](){/*using foo_obj*/});
//....


и тогда уничтожиться?
выходит как бы деструктор скопированных переменных никогда не вызовется? если там написать = то будет как бы утечка памяти, так что ли?
эта функция post возращает управление сразу, а код этот исполняет потом, когда будет возможно (там мутексы особождаются и все такое).
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Iron Bug
  опции профиля:
сообщение 23.10.2013, 20:39
Сообщение #4


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


после исчезновения последнего обращения unique_ptr уничтожит объект, на который он указывает.
его нельзя передавать по указателю, только по значению или ссылке (лучше - константной). иначе весь смысл работы с unique_ptr теряется и нарушается потокобезопасность и разграниченность доступа.

с лямбдами всё сложнее. в новом стандарте C++14 обсуждается вопрос апгрейда лямбда-функций, чтобы они напрямую принимали move (см. пункт Lambda generalized capture). но в С++11 это пока не реализовано.

а в качестве решения для С++11 можно сделать так:
#include <iostream>
#include <memory>

using namespace std;

class foo
{
    // какой-то класс
public:
    ~foo() { cout << "Good bye, C++11" << endl; }  // деструктор, вызывается автоматически при удалении unique_ptr
    void bar() { cout << "Hello, C++11!" << endl; }
};

class test
{
public:
    test() : _str("From test: ") {}
    void go()
    {
        const std::unique_ptr<foo> pfoo(new foo());  // используем константный указатель, чтобы объект никто не уничтожил случайно

        [this,&pfoo](){ pfoo->bar(); }(); // простой вызов

        call_func([this,&pfoo](){ pfoo->bar(); });  // передача в качестве параметра, с this
    }

private:
    string _str;
    void call_func(std::function<void (void)>f){
        cout << _str; // используем this
        f(); // используем указатель на функцию
    }
};

int main()
{
    test t;
    t.go();
    return 0;
}

если я правильно поняла твою задачу :)
обрати внимание на const std::unique_ptr<foo>. это гарантия, чтобы внутри функции твой объект никто не убил.

P.S. тут такие кренделя С++11, что можно запутаться :)

Сообщение отредактировал Iron Bug - 23.10.2013, 21:09
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Iron Bug
  опции профиля:
сообщение 23.10.2013, 22:04
Сообщение #5


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


ещё дополнение:
хорошая статья про unique_ptr.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
alexy
  опции профиля:
сообщение 23.10.2013, 23:29
Сообщение #6


Студент
*

Группа: Участник
Сообщений: 44
Регистрация: 4.8.2010
Пользователь №: 1931

Спасибо сказали: 0 раз(а)




Репутация:   0  


Цитата(Iron Bug @ 23.10.2013, 21:39) *
если я правильно поняла твою задачу :)

не совсем...
в твоем коде указатель используется прямо на месте, то есть любой умный указатель в принцепе подойтед. функция go завершит исполнение и объект уничтожится.. я попробовал передать копии shared_ptr внути go, думал упадет, но результат был тот же - объект также уничтожался по завершении функции go.

а мне нужно чтобы эту лямюду "запостить" в поток. этот поток вызовет её уже тогда, когда функция, "запостившая" и создавшая этот объект, закончит выполнение. объект, созданный в функции оператором new должен удалиться тогда, когда завершит исполнение последняя лямбда функция (иначе крах - обратиться к удаленному объекту).
сами объекты потокобезопасные..

Цитата(Iron Bug @ 23.10.2013, 21:39) *
P.S. тут такие кренделя С++11, что можно запутаться :)

да уж.. ногу сломаешь ))


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

а std::bind я так понимаю уничтожает привязанные боъекты тогда, когад уничтожается объект, возращаенной это функцией, так?
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Iron Bug
  опции профиля:
сообщение 24.10.2013, 0:31
Сообщение #7


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


Цитата(alexy @ 24.10.2013, 2:29) *
в твоем коде указатель используется прямо на месте, то есть любой умный указатель в принцепе подойтед. функция go завершит исполнение и объект уничтожится..

никто тебе не запрещает передавать объект в go() извне, а внутри передавать его дальше. я просто привела простой пример.
go() там, кстати, только для демонстрации использования this в лямбда-функциях (у тебя он зачем-то прилеплен в вызовах).

если в этот пример подставить shared_ptr, то он будет потокобезопасный и у него может быть сколько угодно копий. я просто думала, что у тебя вызовы последовательно используют объект.
Внимание! "потокобезопасность" shared_ptr - это лишь безопасность обращений к самому указателю: безопасный счётчик ссылок и вызов деструктора объекта. сам объект, на который он указывает, не защищён. и если в разных потоках происходит изменение этого объекта, то надо ставить мьютексы или ещё какие-то средства синхронизации для обращения к памяти.

В случае простой передачи данных (функции thread_func - просто заглушки в том месте, где нужно вызвать создание потока):
#include <iostream>
#include <memory>

using namespace std;

class foo
{
    // какой-то класс
public:
    ~foo() { cout << "Good bye, C++11" << endl; }  // деструктор, вызывается автоматически при удалении shared_ptr
    foo() { cout << "Hello, C++11!" << endl; }   // конструктор
    string say_hello() { return "Hello!"; }
};

void thread_func(const shared_ptr<foo> pfoo,std::function<string (const shared_ptr<foo> pfoo)>f)
{
    // здесь можно создать поток и передать в него параметры, а потом их как-то вызвать
    cout << f(pfoo) << endl;
}

int main()
{
    const shared_ptr<foo> pfoo(new foo());
    thread_func(pfoo,[&pfoo](const shared_ptr<foo> pfoo)->string { return string("Func1: ") + pfoo->say_hello(); });
    thread_func(pfoo,[&pfoo](const shared_ptr<foo> pfoo)->string { return string("Func2: ") + pfoo->say_hello(); });
    return 0;
}


Вот реализация с бустовскими потоками (проверено, это работает):
Раскрывающийся текст
#include <iostream>
#include <memory>
#include <boost/thread.hpp>

using namespace std;
using namespace boost;

class foo
{
    // какой-то класс
public:
    ~foo() { cout << "Good bye, C++11" << endl; }  // деструктор, вызывается автоматически при удалении shared_ptr
    foo() { cout << "Hello, C++11!" << endl; } // конструктор
    string say_hello() { return "Hello!"; }
};

void thread_func(int delay,const std::shared_ptr<foo> pfoo,std::function<string (const std::shared_ptr<foo> pfoo)>f)
{
    this_thread::sleep(posix_time::milliseconds(delay)); // создаём вид бурной деятельности - спим! :)
    cout << f(pfoo) << endl; // вызываем нашу лямбду с объектом
}

thread *pthrd1;
thread *pthrd2;

void main_thread()
{
    const std::shared_ptr<foo> pfoo(new foo());
    pthrd1 = new thread(thread_func,3000,pfoo,[&pfoo](const std::shared_ptr<foo> pfoo)->string { return string("Func1: ") + pfoo->say_hello(); });  // этот поток будет выполняться 3 секунды
    pthrd2 = new thread(thread_func,5000,pfoo,[&pfoo](const std::shared_ptr<foo> pfoo)->string { return string("Func2: ") + pfoo->say_hello(); });  // этот поток будет выполняться 5 секунд
}

int main()
{
    thread main_thrd(main_thread);
    main_thrd.join();
    cout << "Main thread finished!" << endl;

    pthrd1->join();
    pthrd2->join();
    return 0;
}


Сообщение отредактировал Iron Bug - 24.10.2013, 18:10
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
alexy
  опции профиля:
сообщение 24.10.2013, 12:12
Сообщение #8


Студент
*

Группа: Участник
Сообщений: 44
Регистрация: 4.8.2010
Пользователь №: 1931

Спасибо сказали: 0 раз(а)




Репутация:   0  


о, работает как надо :) спасибо
объект в shared_ptr уничтожается когда завершаются потоки его использующие.

да, класс который я использую разумеется сам потокобезопасный.. там мутексы boost::signals2 и. т.д.

зы: почему-то не работал без -Wl,--no-as-needed при линковке с потоками из std.. не подключал pthread?

Сообщение отредактировал alexy - 24.10.2013, 12:14
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Iron Bug
  опции профиля:
сообщение 24.10.2013, 18:23
Сообщение #9


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


Цитата(alexy @ 24.10.2013, 15:12) *
объект в shared_ptr уничтожается когда завершаются потоки его использующие.

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

Цитата(alexy @ 24.10.2013, 15:12) *
зы: почему-то не работал без -Wl,--no-as-needed при линковке с потоками из std.. не подключал pthread?

под чем собираешь? с какими опциями? иногда у конкретных версий-систем бывают разные особенности. ну и сторонние библиотеки можно по-разному собирать.

Сообщение отредактировал Iron Bug - 24.10.2013, 18:49
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Iron Bug
  опции профиля:
сообщение 24.10.2013, 20:53
Сообщение #10


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


Цитата(alexy @ 24.10.2013, 2:29) *
а std::bind я так понимаю уничтожает привязанные боъекты тогда, когда уничтожается объект, возращаенной это функцией, так?

bind ничего не делает сам. существование объектов должен обеспечить программист. то есть, если ты передал в bind какой-то объект, то он должен существовать во время вызова полученного функтора. shared_ptr при связывании увеличит счётчик и объект не уничтожится, пока вызов не пройдёт.

вот пример передачи shared_ptr в bind, с потоками:
Раскрывающийся текст
#include <iostream>
#include <functional>
#include <thread>
#include <chrono>

using namespace std;

class foo
{
public:
    void tell_value(const int& val)
    {
        cout << "Value is " << val << endl;
    }
   foo() { cout << "foo: ctor" << endl; }
   ~foo() { cout << "foo: dtor" << endl; }
};

thread *pthrd;

void thread_func(function <void (const int&)> f, const int &arg)
{
    this_thread::sleep_for(chrono::milliseconds(1000));
    f(arg);
}

void func(const shared_ptr<foo> pfoo, const int &val)
{
   pfoo->tell_value(val);
}

void main_thread()
{
    pthrd = new thread(thread_func,bind(func,shared_ptr<foo>(new foo()),placeholders::_1),10);
    cout << "Main thread finished" << endl;
}

int main()
{
   thread main_thrd(main_thread);
   main_thrd.join();
   pthrd->join();
}


на этот раз не стала писать по старой привычке, через boost :), сделала всё через std. хотя я не сравнивала скорость реализаций. это ещё открытый вопрос.

Сообщение отредактировал Iron Bug - 24.10.2013, 21:00
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение

2 страниц V   1 2 >
Быстрый ответОтветить в данную темуНачать новую тему
Теги
Нет тегов для показа


1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0




RSS Текстовая версия Сейчас: 30.3.2024, 7:45