2017-04-06 38 views
2

想象以下代码:如何保持shared_ptr对象的成员在异步函数中使用?

void async(connection *, std::function<void(void)>); 

void work() 
{ 
    auto o = std::make_shared<O>(); 
    async(&o->member, [] { do_something_else(); }); 
} 

async将,例如,开始使用的omember将其作为一个指针通过的螺纹。但是,当oasync()被调用后立即超出范围,并且它将被删除,成员也将被删除。

如何正确和很好地解决这个问题(!)?

显然one solution是通过o到捕获列表。即使不使用,捕获也保证不被优化。

async(&o->member, [o] { do_something_else(); }); 

然而,最近的编译器(铛-5.0)包括-Wextra集合中的-Wunused-lambda-capture。这种情况下会产生未使用的lambda捕获警告。

我在lamdba里面加了(void) o;,它使这个警告无声无息。

async(&o->member, [o] { 
     (void) o; 
     do_something_else(); 
    }); 

是否有更优雅的方式来解决这个问题的范围?

(这个问题的起源是从using write_async of boost::asio派生)

+0

是'member'静态函数取'的std ::功能'?这是我能想到的唯一可能性,在这种情况下,“o”甚至不需要保持活力。 –

+0

假设为了简单起见'member'是一个'int'。 –

+0

那么int *是一个可调用函数? –

回答

0

不是一个伟大的答案,但...

它似乎并不像有一定有“更好” /“干净”的解决方案,尽管我建议更多的“自我描述”解决方案可能是为显式绑定成员函数和其中的shared_ptr实例的线程操作创建一个函子。使用虚拟lambda表达式捕获并不一定能捕捉到这个意图,有人可能会稍后再来,并将其“优化”到一个糟糕的结局。诚然,绑定函子与shared_ptr的语法稍微复杂一些。我的2c,无论如何(我已经做了类似于我的建议,供参考)。

0

我在a project of mine中使用的解决方案是从enable_shared_from_this派生类并让它通过存储所述共享指针的一个拷贝的数据成员的异步呼叫期间泄漏
参见Resource class了解更多细节,特别是成员方法leakreset
一旦清理它看起来像下面的小例子:

#include<memory> 

struct S: std::enable_shared_from_this<S> { 
    void leak() { 
     ref = this->shared_from_this(); 
    } 

    void reset() { 
     ref.reset(); 
    } 

private: 
    std::shared_ptr<S> ref; 
}; 

int main() { 
    auto ptr = std::make_shared<S>(); 
    ptr->leak(); 
    // do whatever you want and notify who 
    // is in charge to reset ptr through 
    // ptr->reset(); 
} 

的主要风险是,如果你从来没有复位内部指针你有一个实际的泄漏。在这种情况下,很容易处理它,因为底层库需要资源在放弃之前显式关闭,并在关闭时重置指针。在此之前,生物资源可以通过适当的功能(walkLoop class的成员函数,仍然是映射到底层库提供的东西)来检索,并且仍然可以随时关闭它们,因此可以完全避免泄漏。
在你的情况下,你必须找到办法避免这个问题,这可能是一个问题,但它主要取决于实际的代码,我不能说。
一个可能的缺点是,在这种情况下,你不得不通过共享指针上创建动态存储你的对象,否则整个事情会爆发并不起作用。

1

我建议你async功能没有优化设计。如果async将来会在某个任意点调用这个函数,并且它需要当时的connection还活着,那么我会看到两种可能性。你可以做任何拥有底层逻辑的东西async也拥有connection。例如:

class task_manager { 
    void async(connection*, std::function<void()> f); 
    connection* get_connection(size_t index); 
}; 

这样,connection将永远活着的时候async被调用。 或者,你可以有async采取unique_ptr<connection>shared_ptr<connection>

void async(std::shared_ptr<connection>, std::function<void()> f); 

这比捕捉connection在封闭的主人,可能有无法预料的副作用(包括更好的是async可能期望connection留在函数对象被调用和销毁之后活着)。

+0

同意,但我不能改变我在现实中使用的异步函数的原型。它是boost :: asio中的一个(参见我的问题结尾处的链接)。 –

2

Boost.Asio似乎suggest使用enable_shared_from_this来保留任何拥有“connection”活着,而有未决的操作使用它。例如:

class task : std::enable_shared_from_this<task> { 
public: 
    static std::shared_ptr<task> make() { 
    return std::shared_ptr<task>(new task()); 
    } 

    void schedule() { 
    async(&conn, [t = shared_from_this()]() { t->run(); }); 
    } 

private: 
    task() = default; 

    void run() { 
    // whatever 
    } 

    connection conn; 
}; 

然后使用task

auto t = task::make(); 
t->schedule(); 

这似乎是一个好主意,因为它封装了所有调度逻辑和执行task本身内的task

+0

这是用asio做*的方式。你可以使用这种方法保持套接字/客户端处理程序等活着,并在不再有异步事件处理它们时允许它们优雅地过期。 – Nim

相关问题