如何将自删除期货存储在列表中
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)
看起来非常危险。
【讨论】:
不仅remove
,push_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 屏幕中删除文件并从存储库中删除该文件? [复制]
IntelliJ IDEA:“Indexed Maven Repositories”列表 - 如何删除此列表中的远程 maven 存储库?