关于函数引用和线程的问题
我在我的虚拟 Linux 机器 (GCC 4.4.5-Debian) 中使用以下测试程序随机测试了 std::thread
:
#include <algorithm>
#include <thread>
#include <iostream>
#include <vector>
#include <functional>
using namespace std;
static int i=0;
void f( vector<int> &test)
{
++i;
cout << "Push back called" << endl;
test.push_back(i);
}
int main()
{
vector<thread> t;
vector<int> test;
for( int i=0; i<1000; ++i )
{
t.push_back( thread( bind(f, test) ) );
}
for( auto it = t.begin(); it != t.end(); ++it )
{
(*it).join();
}
cout << test.size() << endl;
for( auto it = test.begin(); it != test.end(); ++it )
{
cout << *it << endl;
}
return 0;
}
为什么向量 test
保持为空?我是否对引用做了一些愚蠢的事情(可能)或者是与 bind
或某些线程问题有关?
谢谢!
更新:在 Kos 和villintehaspan 的共同帮助下,我“修复”了“问题”:
#include <algorithm>
#include <thread>
#include <iostream>
#include <vector>
#include <functional>
using namespace std;
static int i=0;
void f( vector<int> &test)
{
++i;
test.push_back(i);
}
int main()
{
vector<thread> t;
vector<int> test;
for( int i=0; i<1000; ++i )
{
t.push_back( thread(f, std::ref(test)) );
}
for( auto it = t.begin(); it != t.end(); ++it )
{
(*it).join();
}
cout << test.size() << endl;
for( auto it = test.begin(); it != test.end(); ++it )
{
cout << *it << endl;
}
return 0;
}
按顺序打印所有值,并且似乎工作正常。现在只剩下一个问题:这只是幸运(又名未定义行为(TM))还是静态变量导致代码中出现类似互斥锁的静默步骤?
PS:我理解这里的“杀死多线程”问题,但这不是我的观点。我只是想测试基本 std::thread
功能的稳健性...
I was randomly testing std::thread
in my virtual linux machine (GCC 4.4.5-Debian) with this test program:
#include <algorithm>
#include <thread>
#include <iostream>
#include <vector>
#include <functional>
using namespace std;
static int i=0;
void f( vector<int> &test)
{
++i;
cout << "Push back called" << endl;
test.push_back(i);
}
int main()
{
vector<thread> t;
vector<int> test;
for( int i=0; i<1000; ++i )
{
t.push_back( thread( bind(f, test) ) );
}
for( auto it = t.begin(); it != t.end(); ++it )
{
(*it).join();
}
cout << test.size() << endl;
for( auto it = test.begin(); it != test.end(); ++it )
{
cout << *it << endl;
}
return 0;
}
Why does vector test
remain empty? Am I doing something stupid with references (probably) or is it something with bind
or some threading problem?
Thanks!
UPDATE: with the combined help of Kos and villintehaspan I "fixed" the "problem":
#include <algorithm>
#include <thread>
#include <iostream>
#include <vector>
#include <functional>
using namespace std;
static int i=0;
void f( vector<int> &test)
{
++i;
test.push_back(i);
}
int main()
{
vector<thread> t;
vector<int> test;
for( int i=0; i<1000; ++i )
{
t.push_back( thread(f, std::ref(test)) );
}
for( auto it = t.begin(); it != t.end(); ++it )
{
(*it).join();
}
cout << test.size() << endl;
for( auto it = test.begin(); it != test.end(); ++it )
{
cout << *it << endl;
}
return 0;
}
Which prints all values in order and seems to work OK. Now only one question remains: is this just lucky (aka undefined behavior (TM) ) or is the static variable causing a silent mutex-like step in the code?
PS: I understand the "killing multithreadedness" problem here, and that's not my point. I'm just trying to test the robustness of the basic std::thread
functionality...
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
在我看来像是线程问题。
虽然我不是 100% 确定,但应该注意的是,所有 1000 个线程:
do
++i
对同一个 int 值(这不是原子操作 - 您可能会在这里遇到问题,您可以使用__sync_fetch_and_add(&i,1)
(请注意,这是一个 gcc 扩展,而不是标准 C++);do < code>push_back 同时在
std::vector
上,这不是线程安全的容器 AFAIK...对于cout
我想我相信你。需要使用一个锁定机制(也许是 std::mutex?到目前为止我只使用过 pthreads,但我相信这就是您所需要的)。请注意,这种情况会消除此处使用线程的任何好处,但这是因为您不应该在非线程安全对象上同时使用多个线程。
----编辑----
我在谷歌上搜索了这个线程 API(不幸的是,Windows 上的 tdm gcc 4.5 上没有)。
显然,而不是:
您可以以这种方式说出
并传递任意数量的参数。
来源:http://www.informit.com/guides/content .aspx?g=cplusplus&seqNum=422
这也应该可以解决你制作我之前没有注意到的向量副本的问题(这里为@villintehaspam+1)。
实际上,还需要一件事来确保此处不创建副本:
将确保向量不被复制。
哇,我也很困惑。 :)
Looks to me like a threading problem.
While I'm not 100% sure, it should be noted that all 1000 threads:
do
++i
on the same int value (it's not an atomic operation- you may encounter problems here, you can use__sync_fetch_and_add(&i,1)
instead (note that it's a gcc extension not standard C++);do
push_back
simultaneously on astd::vector
, which is not a thread-safe container AFAIK... Same forcout
I think. I believe you'd need to use a locking mechanism around that (std::mutex
perhaps? I've only used pthreads so far but I believe it's what you need).Note that this kind of kills any benefit of using threads here, but that's a consequence of the fact that you shouldn't use multiple threads at once on a non-thread-safe object.
----EDIT----
I had a google on this threading API (not present on my tdm gcc 4.5 on Windows, unfortunately).
Aparrently instead of:
you can just say
and pass an arbitrary number of arguments in this way.
Source: http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=422
This should also solve your problem with making a copy of the vector which I haven't noticed before(+1 for @villintehaspam here).
Actually, one more thing is needed to make sure the copy isn't created here:
will make sure that the vector isn't copied.
Wow, I got confused too. :)
绑定实际上会创建向量的副本,以便每个线程push_back都在自己的副本上(是的,&在这里没有帮助)。您需要为线程提供指针或类似的指针,以便它们使用相同的向量。您还应该确保使用 Kos 建议的访问保护。
编辑:修复使用 std::ref 而不是复制向量后,多线程访问问题仍然存在。我的猜测是,你现在没有遇到任何问题的唯一原因是因为这个例子是如此微不足道(或者也许你只在调试模式下尝试过) - 没有自动保证 ++ 是原子的,只是因为int 是静态的。
The bind will actually make a copy of the vector, so that each thread push_back's on their own copy (yes, that & won't help here). You need to provide the threads with a pointer or similar so that they use the same vector. You should also make sure to use access protection like suggested by Kos.
Edit: After your fix to use std::ref instead of making a copy of the vector, the multithreaded access problem still remains. My guess is that the only reason you don't get any problems right now is because the example is so trivial (or maybe you've only tried in debug mode) - there is no automatic guarantee that the ++ is atomic just because the int is static.