boost :: threads执行顺序

时间:2010-09-03 11:50:07

标签: c++ multithreading boost mutex boost-thread

我对连续创建的线程的执行顺序有疑问。 这是代码。

#include <iostream>
#include <Windows.h>
#include <boost/thread.hpp>

using namespace std;

boost::mutex mutexA;
boost::mutex mutexB;
boost::mutex mutexC;
boost::mutex mutexD;


void SomeWork(char letter, int index)
{
    boost::mutex::scoped_lock lock;
    switch(letter)
    {
    case 'A' : lock = boost::mutex::scoped_lock(mutexA); break;
    case 'B' : lock = boost::mutex::scoped_lock(mutexB); break;
    case 'C' : lock = boost::mutex::scoped_lock(mutexC); break;
    case 'D' : lock = boost::mutex::scoped_lock(mutexD); break;
    }

    cout << letter <<index << " started" << endl;
    Sleep(800);
    cout << letter<<index << " finished" << endl; 
}

int main(int argc , char * argv[])
{
    for(int i = 0; i < 16; i++)
    {
        char x = rand() % 4 + 65;
        boost::thread tha = boost::thread(SomeWork,x,i);
        Sleep(10);
    }
Sleep(6000);
    system("PAUSE");
  return 0;
}

每次将一个字母(从A到D)和一个genereaion id(i)作为一个线程传递给SomeWork方法。我不关心字母之间的执行顺序,但对于一个特殊的字母,比如A,Ax必须在Ay之前开始,如果x&lt;年。 随机输出代码的随机部分是:

B0 started  
D1 started  
C2 started  
A3 started  
B0 finished  
B12 started  
D1 finished  
D15 started  
C2 finished  
C6 started  
A3 finished  
A9 started
B12 finished
B11 started --> B11 started after B12 finished.
D15 finished
D13 started
C6 finished
C7 started
A9 finished

如何避免这种情况?
感谢。


我使用条件变量解决了问题。但我改变了一点问题。解决方案是跟踪for循环的索引。所以每个线程都知道什么时候不起作用。但就这个代码而言,还有两件我想问的问题 首先,在我的计算机上,当我将for循环索引设置为350时,我有一个访问冲突。 310是循环次数,这没关系。所以我意识到要生成最大数量的线程。我怎么能确定这个数字? 第二,在visual studio 2008中,代码的发布版本显示了一种非常奇怪的行为。不使用条件变量(第1行到第3行被注释掉),线程被排序。怎么会发生这种情况?

这是代码:

#include <iostream>
#include <Windows.h>
#include <boost/thread.hpp>

using namespace std;

boost::mutex mutexA;
boost::mutex mutexB;
boost::mutex mutexC;
boost::mutex mutexD;


class cl
{
public:
    boost::condition_variable con;
    boost::mutex mutex_cl;
    char Letter;
    int num;
    cl(char letter) : Letter(letter) , num(0)
    {

    }
    void doWork( int index, int tracknum)
    {
        boost::unique_lock<boost::mutex> lock(mutex_cl);
        while(num != tracknum)     // line 1
            con.wait(lock);   // line 2 
        Sleep(10);
        num = index;
        cout << Letter<<index << endl; 
        con.notify_all();  // line 3
    }
};

int main(int argc , char * argv[])
{
    cl A('A');
    cl B('B');
    cl C('C');
    cl D('D');

    for(int i = 0; i < 100; i++)
    {   
        boost::thread(&cl::doWork,&A,i+1,i);
        boost::thread(&cl::doWork,&B,i+1,i);
        boost::thread(&cl::doWork,&C,i+1,i);
        boost::thread(&cl::doWork,&D,i+1,i);
    }
    cout << "************************************************************************" << endl;

    Sleep(6000);
    system("PAUSE");
  return 0;
}

3 个答案:

答案 0 :(得分:6)

如果你有两个不同的线程在等待锁定,那么一旦前一个持有者释放锁定,它将完全不确定。我相信这正是你所经历的。假设B10持有锁,同时产生B11B12的线程。 B10释放锁定 - 无论先前创建了哪个线程,或者甚至是哪个线程首先开始等待,都会向{3}}或B11接下来获取它。 / p>

也许你应该为每个字母实现工作队列,这样你就会产生4个线程,每个线程都消耗工作单位?这是以这种方式轻松保证订购的唯一方法。如果多个线程正在等待锁定,那么简单的互斥锁不会保证排序。

答案 1 :(得分:2)

即使B11在B12之前启动,也不能保证在B12之前执行一些CPU时间片来执行SomeWork()。这个决定取决于操作系统及其调度程序。

Mutex通常用于同步线程之间的数据访问,并且已经引起了线程执行序列(即数据访问)的问题。

如果组'A'的线程在相同数据上执行相同的代码,那么只需使用一个线程。这将消除组中线程之间的上下文切换并产生相同的结果。如果数据正在改变,则考虑生产者/消费者模式。 Paul Bridger给出了一个易于理解的生产者/消费者示例here

答案 2 :(得分:1)

您的线程具有在开始执行之前必须满足的依赖项。在您的示例中,B12取决于B0和B11。不知何故,你必须追踪这种依赖性知识。必须使具有未完成依赖项的线程等待。

我会调查condition variables。每次线程完成SomeWork()时,它都会使用条件变量的notify_all()方法。然后,所有等待的线程必须检查它们是否仍然具有依赖性。如果是这样,请回去等待。否则,请继续调用SomeWork()。

每个线程都需要一些方法来确定它是否有未完成的依赖项。这可能是一些全球可用的实体。您应该只在拥有互斥锁时修改它(在SomeWork()中)。对于简单的数据结构,多线程读取应该是安全的。