【问题标题】:boost::threads execution orderingboost::threads 执行顺序
【发布时间】:2011-04-07 19:36:59
【问题描述】:

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

#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,如果 x B0 开始 D1 开始 C2 开始 A3开始 B0 完成 B12开始 D1 完成 D15 开始 C2完成 C6开始 A3成品 A9开始 B12完成 B11 开始 --> B11 在 B12 完成后开始。 D15完成 D13 开始 C6完成 C7开始 A9完成

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


我使用条件变量解决了这个问题。但我稍微改变了问题。解决方案是跟踪 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;
}

【问题讨论】:

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


    【解决方案1】:

    如果您有两个不同的线程在等待锁,那么一旦前一个持有者释放锁,哪个线程将获取它是完全不确定的。我相信这就是你正在经历的。假设B10 持有锁,同时为B11B12 生成线程。 B10 释放锁 - 至于是 B11 还是 B12 下一个获取它,这取决于抛硬币,无论哪个线程首先创建,甚至哪个线程首先开始等待。

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

    【讨论】:

    • 某个线程要完成的工作的一些表示。在这种情况下,一个工作单元基本上是一对(letter,index)
    【解决方案2】:

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

    互斥锁通常用于在线程之间同步对数据的访问,并且已经对线程执行的顺序(即数据访问)提出了问题。

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

    【讨论】:

      【解决方案3】:

      您的线程具有在开始执行之前必须满足的依赖关系。在您的示例中,B12 取决于 B0 和 B11。您必须以某种方式跟踪该依赖关系知识。必须使具有未完成依赖关系的线程等待。

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-11-25
        • 1970-01-01
        • 1970-01-01
        • 2011-03-12
        • 2011-12-20
        • 1970-01-01
        相关资源
        最近更新 更多