boost :: упорядочение выполнения потоков - PullRequest
5 голосов
/ 03 сентября 2010

У меня проблема с порядком выполнения потоков, созданных последовательно.вот код.

#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) и идентификатор зоны (i) передаются методу SomeWork как поток.меня не волнует порядок выполнения между буквами, но для букв в форме буквы, скажем, A, Ax должен начинаться раньше Ay, если x

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 ]

6 голосов
/ 03 сентября 2010

Если у вас есть два разных потока, ожидающих блокировку, то совершенно недетерминировано, какой из них получит ее, как только блокировка будет снята предыдущим держателем.Я считаю, что это то, что вы испытываете.Предположим, что B10 удерживает блокировку, и в то же время потоки создаются для B11 и B12.B10 снимает блокировку - все зависит от того, получит ли B11 или B12 следующий, независимо от того, какой поток был создан первым или какой поток начал ждать первым.Вы должны реализовать рабочие очереди для каждой буквы, так что вы создаете ровно 4 потока, каждый из которых потребляет рабочие единицы?Это единственный способ легко гарантировать заказ таким способом.Простой мьютекс не гарантирует порядок, если несколько потоков ожидают блокировки.

2 голосов
/ 03 сентября 2010

Даже несмотря на то, что B11 запускается до B12, не гарантируется, что будет выделен временной интервал ЦП для выполнения SomeWork () до B12.Это решение зависит от ОС и ее планировщика.

Мьютекс обычно используется для синхронизации доступа к данным между потоками, и возникла проблема с последовательностью выполнения потоков (т. Е. С доступом к данным).

Если потоки для группы 'A' выполняют один и тот же код для одних и тех же данных, тогда просто используйте один поток.Это исключит переключение контекста между потоками в группе и даст тот же результат.Если данные меняются, рассмотрите модель производителя / потребителя.Пол Бриджер приводит простой для понимания пример производителя / потребителя здесь .

1 голос
/ 03 сентября 2010

Ваши потоки имеют зависимости, которые должны быть удовлетворены, прежде чем они начнут выполнение.В вашем примере B12 зависит от B0 и B11.Каким-то образом вы должны отслеживать это знание зависимости.Потоки с незавершенными зависимостями должны ждать.

Я бы посмотрел на условные переменные .Каждый раз, когда поток завершает SomeWork (), он будет использовать метод notify_all () условной переменной.Затем все ожидающие потоки должны проверить, есть ли у них зависимости.Если так, вернись и подожди.В противном случае, продолжайте и вызывайте SomeWork ().

Вам необходимо каким-то образом для каждого потока определить, имеет ли он незавершенные зависимости.Вероятно, это будет какая-то глобально доступная сущность.Вы должны изменять его только тогда, когда у вас есть мьютекс (в SomeWork ()).Чтение несколькими потоками должно быть безопасным для простых структур данных.

...