请教一个 shared_ptr 内存泄漏的问题

2022-05-10 11:19:39 +08:00
 guang19

如题,我正在用 cpp 写一个线程池的 submit 函数,为了可以让用户可以异步的获取到函数执行的返回值,我使用了 future 和 promise 特性,一部分代码如下:

template <typename Fn, typename... Args, typename HasRet, typename Redundant>
::std::future<typename ::std::result_of<Fn(Args...)>::type>
ThreadPool::submit(Fn&& func, Args... args)
{
	if (!running_)
	{
		LOGS_FATAL << "thread pool has not been initialized";
	}
	using RetType = typename ::std::result_of<Fn(Args...)>::type;
	LockGuardType lock(mutex_);
	auto promise = ::std::make_shared<::std::promise<RetType>>();
	auto future = promise->get_future();
	::std::function<RetType()> tmpTask = ::std::bind(::std::forward<Fn>(func), 					::std::forward<Args>(args)...);
1 )	TaskType task = [t = ::std::move(tmpTask), promise]() mutable
	{
2 )		assert(promise.use_count() == 1);
		promise->set_value(t());
	};
3 )    assert(promise.use_count() == 2);
    ......
    return future;
}

1 )处我用的 lambda 是值捕获 promise,3 )处的 use_count 总为 2 这没问题,现在的问题不知为什么 2 )处的 use_count 却不稳定,有时为 3 ,有时为 1 ? 下面为测试代码:

TEST_F(ThreadPoolTest, SubmitHasRetval)
{
	::std::function<int32_t ()> f = []() -> int32_t
	{
		return 5;
	};
	auto future1 = threadPool->submit(f);
	future1.wait();
	ASSERT_EQ(future1.get(), 5);
	auto future2 = threadPool->submit(::std::move(f));
	future2.wait();
	ASSERT_EQ(future2.get(), 5);
}

debug 模式下正常结果为:

2022-05-10 11:09:44.942 [9924] DEBUG ThreadPool.cpp:111 - thread pool init success
2022-05-10 11:09:44.943 [9924] DEBUG ThreadPool.cpp:128 - thread pool has been shutdown

错误结果为:

... Assertion `promise.use_count() == 1' failed.
2022-05-10 11:17:26.951 [10115] DEBUG ThreadPool.cpp:111 - thread pool init success

感谢大佬们看完我这丑陋的代码。。。

1877 次点击
所在节点    程序员
22 条回复
nightwitch
2022-05-11 00:01:21 +08:00
在多线程环境下不要用 shared_ptr 的 use_count()以及 uniqiue() (已经在 C++20 被删除)这个 API 。
cppreference 上明确标记了这两个 API 不保证线程安全,所以在多线程环境下其只返回一个可能的值,不是精确的。

reference:https://en.cppreference.com/w/cpp/memory/shared_ptr/use_count

@wzzzx 好问题,除了不会自动 join 以外,std::thread 也不能被打断或者取消,这个问题在 C++20 得到了修复。标准库加入了 std::jthread ,见 https://en.cppreference.com/w/cpp/thread/jthread
ColorfulBoar
2022-05-11 01:07:11 +08:00
@wzzzx joinable()只是检测这个 std::thread object 是不是依然持有一个进程而已别的啥都没干,或许你想问的是为什么 std::thread 的 destructor 这么奇怪……当然不管想问的是啥,不管把标准还是实现重复一遍肯定都没意思,你一定想听个刺激的,所以让我恶意转述一下史官 Bjarne Stroustrup 的说法:我们当年也不想这样,但活在 [世界上只有 C 语言 + POSIX 的 P 意思是 portable] 这个梦里的 C 标准委员会因为 C 里面没有 RAII / POSIX 天生残疾就以为别人也不行,强烈反对我们干人事(这是唯一一份 C 标准委员会发给 C++标准委员会的正式通知),那就别怪我们摆烂了。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/851924

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX