如何将自删除期货存储在列表中

Posted

技术标签:

【中文标题】如何将自删除期货存储在列表中【英文标题】:How to store self-removing futures in a list 【发布时间】:2019-03-18 10:55:19 【问题描述】:

我有一些任务需要异步执行,当还有任务在运行时服务器无法关闭。所以我试图将std::async 返回的期货存储在一个列表中,但我也不想得到一个无限增长的列表。所以我想在期货完成后删除它们。

这大概是我想要做的:

// this is a member of the server class
std::list<std::future<void>> pending;

std::list<std::future<void>>::iterator iter = ???;

pending.push_back( std::async( std::launch::async, [iter]()

    doSomething();
    pending.remove( iter );
 );

这里,iter 需要指向新插入的元素,但是在插入元素之前(没有迭代器)我无法获取它,也无法在之后(因为它是通过值传递给 lambda)。我可以创建一个shared_ptr 来存储迭代器,但这似乎有点矫枉过正。

有更好的模式吗?

更新:这似乎还有另一个问题。当未来试图从列表中删除自己时,它实际上是在等待自己完成,这会锁定所有内容。糟糕!

最重要的是,列表析构函数在调用元素析构函数之前清空列表。

【问题讨论】:

你知道这段代码无论如何都需要某种同步,比如互斥锁吗? 你的意思是remove?是的,我需要这样的东西。推送操作仅从套接字侦听器调用,因此应该已经同步。 我会小心的。 C++ 内存模型不保证在没有内存栅栏的情况下线程之间更改的可见性 哦,对了,无论如何我都需要将推送与删除同步,所以不妨在任何地方放置一个互斥锁。 或者使用无锁列表,不管用什么方法 【参考方案1】:

看来你可以在列表中附加一个默认的std::future,获取一个迭代器,然后移动你的未来。

请注意,不受互斥体保护的 remove(iter) 看起来非常危险。

【讨论】:

不仅removepush_back也需要同步。 @DanielLangr:这是一种解决方案。另一种是放弃futures删除自己的想法,而是让同一个线程执行push_back和erase。 其实我现在想想,未来是否有可能将自己从列表中删除?它不会把所有东西都锁起来,因为它会等待自己完成吗? @riv:你检查过 *** 上是否有关于这个的问题吗?因为这是一个很好的问题,不应该被埋没在评论中。 我正在尝试看看自己会发生什么,但我收到了一个运行时错误,说迭代器无法递增,所以一旦我弄清楚这意味着什么,我可能会问一个问题。跨度> 【参考方案2】:

这是一种方法。我不认为这个需要期货:

#include <unordered_set>
#include <condition_variable>
#include <mutex>
#include <thread>

struct server

    std::mutex pending_mutex;
    std::condition_variable pending_condition;
    std::unordered_set<unsigned> pending;
    unsigned next_id =  0;

    void add_task()
    
        auto lock = std::unique_lock(pending_mutex);
        auto id = next_id++;
        auto t = std::thread([this, id]
            this->doSomething();
            this->notify_complete(id);
        );
        t.detach(); // or we could store it somewhere. e.g. pending could be a map
        pending.insert(id);
    

    void doSomething();

    void notify_complete(unsigned id)
    
        auto lock = std::unique_lock(pending_mutex);
        pending.erase(id);
        if (pending.empty())
            pending_condition.notify_all();
    

    void wait_all_complete()
    
        auto none_left = [&]  return pending.empty(); ;

        auto lock = std::unique_lock(pending_mutex);
        pending_condition.wait(lock, none_left);
    
;


int main()

    auto s = server();
    s.add_task();
    s.add_task();
    s.add_task();
    s.wait_all_complete();

这里是期货,以防万一:

#include <unordered_map>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <future>

struct server

    std::mutex pending_mutex;
    std::condition_variable pending_condition;
    std::unordered_map<unsigned, std::future<void>> pending;
    unsigned next_id =  0;

    void add_task()
    
        auto lock = std::unique_lock(pending_mutex);
        auto id = next_id++;
        auto f = std::async(std::launch::async, [this, id]
            this->doSomething();
            this->notify_complete(id);
        );
        pending.emplace(id, std::move(f));
    

    void doSomething();

    void notify_complete(unsigned id)
    
        auto lock = std::unique_lock(pending_mutex);
        pending.erase(id);
        if (pending.empty())
            pending_condition.notify_all();
    

    void wait_all_complete()
    
        auto none_left = [&]  return pending.empty(); ;

        auto lock = std::unique_lock(pending_mutex);
        pending_condition.wait(lock, none_left);
    
;


int main()

    auto s = server();
    s.add_task();
    s.add_task();
    s.add_task();
    s.wait_all_complete();

这是列表版本:

#include <list>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <future>

struct server

    using pending_list = std::list<std::future<void>>;
    using id_type = pending_list::const_iterator;

    std::mutex pending_mutex;
    std::condition_variable pending_condition;
    pending_list pending;

    void add_task()
    
        auto lock = std::unique_lock(pending_mutex);

        // redundant construction
        auto id = pending.emplace(pending.end());
        auto f = std::async(std::launch::async, [this, id]
            this->doSomething();
            this->notify_complete(id);
        );
        *id = std::move(f);
    

    void doSomething();

    void notify_complete(id_type id)
    
        auto lock = std::unique_lock(pending_mutex);
        pending.erase(id);
        if (pending.empty())
            pending_condition.notify_all();
    

    void wait_all_complete()
    
        auto none_left = [&]  return pending.empty(); ;

        auto lock = std::unique_lock(pending_mutex);
        pending_condition.wait(lock, none_left);
    
;


int main()

    auto s = server();
    s.add_task();
    s.add_task();
    s.add_task();
    s.wait_all_complete();

【讨论】:

第二个版本与我正在尝试做的大致相同,除了它使用地图而不是列表,我认为这是不必要的,因为 ID 对我来说毫无意义,所以不妨使用迭代器. 当然可以使用列表。尽管使用地图的开销很小。您需要默认构造地图条目和。将未来移入。这可能需要一些异常处理以确保完整性。我发现地图解决方案在现实世界中非常易于维护和灵活。付你的钱,做你的选择。 :) @riv 为完整性添加了列表版本。

以上是关于如何将自删除期货存储在列表中的主要内容,如果未能解决你的问题,请参考以下文章

如何从字符串列表中删除引号并将其再次存储为列表..?

如何从 git bash 屏幕中删除文件并从存储库中删除该文件? [复制]

如何(批量)删除Amazon S3存储桶中几百个文件的列表

如何删除ngrx中以前的存储状态?

IntelliJ IDEA:“Indexed Maven Repositories”列表 - 如何删除此列表中的远程 maven 存储库?

将来尝试引用已删除的函数