关于函数引用和线程的问题

时间:2011-01-02 13:03:03

标签: c++ multithreading c++11 reference bind

我使用此测试程序在我的虚拟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;
}

为什么vector 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功能的稳健性......

2 个答案:

答案 0 :(得分:4)

在我看来就像是一个线程问题。

虽然我不是100%肯定,但应该注意所有1000个线程:

  • 对同一个int值执行++i(这不是原子操作 - 你可能会遇到问题,你可以使用__sync_fetch_and_add(&i,1)代替(注意它是gcc扩展而不是标准C ++)

  • push_back上同时执行std::vector,这不是一个线程安全的容器AFAIK ...我认为cout相同。我相信你需要使用锁定机制(std::mutex或许?到目前为止我只使用了pthreads,但我相信这就是你所需要的。)

请注意,这种方式会在此处使用线程的任何好处,但这是因为您不应该在非线程安全对象上一次使用多个线程。


---- ---- EDIT

我在这个线程API上有一个谷歌(不幸的是,我在Windows上的tdm gcc 4.5上没有)。 显然不是:

thread( bind(f, test) )
你可以说

thread( f, test )

以这种方式传递任意数量的参数。

来源:http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=422

这也应该解决你在制作我之前没有注意到的载体副本的问题  (此处为@villintehaspam +1)。


实际上,还需要做一件事来确保这里没有创建副本:

thread( f, std::ref(test) )

将确保不复制向量。

哇,我也很困惑。 :)

答案 1 :(得分:3)

bind实际上会创建一个vector的副本,这样每个线程push_back都会自己复制(是的,那个&amp;将无助于此)。您需要为线程提供指针或类似指针,以便它们使用相同的向量。您还应该确保使用Kos建议的访问保护。

编辑:在修复使用std :: ref而不是制作向量副本之后,多线程访问问题仍然存在。我的猜测是你现在没有遇到任何问题的唯一原因是因为这个例子非常简单(或者你只是在调试模式下尝试过) - 没有自动保证++是原子的,因为int是静态的。