代码之家  ›  专栏  ›  技术社区  ›  riv

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

  •  1
  • riv  · 技术社区  · 7 年前

    我有一些任务需要异步执行,并且服务器在仍有任务运行时无法关闭。所以我想储存 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 存储迭代器,但这似乎太过分了。

    有更好的模式吗?

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

    除此之外,列表析构函数清空列表 之前 调用元素析构函数。

    0 回复  |  直到 7 年前
        1
  •  7
  •   MSalters    7 年前

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

    注意,非互斥保护 remove(iter) 看起来非常危险。

        2
  •  1
  •   Richard Hodges    7 年前

    这是一条路。我认为这个不需要未来:

    #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();
    }