Brak rozumienia std::thread i lambdy czy bugi MinGW

0

Fragment kodu:

#ifdef USE_THREAD
            thread th[ThreadCount];
#endif
            for(unsigned t=0;t<ThreadCount;++t)
              {
#ifdef USE_THREAD
               th[t]=thread
                 (
                  [t]
#endif
                    {
                     for(unsigned y=t;y<ColumnCount;y+=ThreadCount)
                       {
                        for(unsigned p=0;p<PairCount;++p)
                          {
                           double add=0,*tcy=TC[p][y];
                           for(unsigned x=0;x<ColumnCount;++x)
                             {
                              add+=tcy[x]*TR[p][x];
                             }
                           for(unsigned s=0;s<ResultCount;++s)
                             {
                              TY[s][y][p]+=add*(SV[s+DeadCount+ForwardCount].X[p]-SV[s+DeadCount].X[p]);
                             }
                          }
                       }
                    }
#ifdef USE_THREAD
                 );
#endif
              }
            ++RowCount;
#ifdef USE_THREAD
            for(unsigned t=0;t<ThreadCount;++t) th[t].join();
#endif

Bez wątków działa to poprawnie, z wątkami zaś po jakimś czasie wyburacza się.
Pytanie - które zdanie jest poprawne?:

  1. Eksperymentalny std::thread od MinGW ma jakiegoś buga.
  2. Ja czegoś nie rozumiem.

Zdaje sobie sprawę że w gruncie rzeczy oba zdania poprawne, ale to bez związku z powyższym kodem.

0

A wykluczyłeś rozumiem błąd w kodzie? ;) Bo widze że operujesz tu na tablicach nie-lokalnych względem wątków i nawet te tablice modyfikujesz bez żadnej sekcji krytycznej. Na pewno tak miało być?

0

To jest fragment metody, tablicę są składowymi klasy, wydaje mi się że wątki modyfikują rozłączne zbiory elementów tablicy.
Nawet gdyby modyfikowałem te same elementy tablicy z różnych wątków to nadal nie powinno to być powodem dla access violation (wg mnie).

0

A jak odpalisz ten program z debugerem to gdzie ci pokazuje że poleciał ten segfault? Albo z valgrindem? One często potrafią pokazać w ktorym miejscu kodu pojawiło się nieprawidłowe odwołanie.

1

Z debugerem to nie bardzo się da, bo to DLL odpalana przez service.
Z valgridem - większych bzdur jeszcze w życiu nie wiedziałem.
Poniżej kod który zrobiłem sobie dla testów - wywala się w losowym miejscu (czasami trzeba trochę poczekać).

#include <iostream>
#include <thread>
using namespace std;

//
#define USE_THREAD
//#define USE_DOUBLE

const unsigned PairCount=18;
const unsigned ThreadCount=16;
const unsigned FiltrCount=64;
const unsigned DeadCount=128;
const unsigned ForwardCount=128;
const unsigned ResultCount=8;
const unsigned SavedCount=DeadCount+ForwardCount+ResultCount;
const unsigned ColumnCount=FiltrCount*2;

#ifdef USE_DOUBLE
struct MatrixData
  {
   double X[PairCount];
   // and more ...
  };
MatrixData SV[SavedCount+1]={{0}};

double TC[PairCount][ColumnCount][ColumnCount]={{{0}}};
double TR[PairCount][ColumnCount]={0};
double TY[ResultCount][ColumnCount][PairCount]={{{0}}};
#else
template<class T> struct VirtualTab
  {
   unsigned Size;
   T value;
   VirtualTab(unsigned Size,const T &value):Size(Size),value(value) { }
   T &operator[](unsigned p)
     {
      if(p>=Size)
        {
         cout<<"A tu cie mam"<<endl;
         cin.get();
        }
      return value;
     }
   const T &operator[](unsigned p)const
     {
      if(p>=Size)
        {
         cout<<"A tu cie mam: "<<p<<">="<<Size<<endl;
         cin.get();
        }
      return &value;
     }
  };

struct MatrixData
  {
   VirtualTab<double> X;
   // and more ...
   MatrixData():X(PairCount,0) {}
  };
  
VirtualTab<MatrixData> SV(SavedCount+1,MatrixData());

VirtualTab<VirtualTab<VirtualTab<double> > > TC(PairCount,VirtualTab<VirtualTab<double> >(ColumnCount,VirtualTab<double>(ColumnCount,0)));
VirtualTab<VirtualTab<double> > TR(PairCount,VirtualTab<double>(ColumnCount,0));
VirtualTab<VirtualTab<VirtualTab<double> > > TY(ResultCount,VirtualTab<VirtualTab<double> >(ColumnCount,VirtualTab<double>(PairCount,0)));
#endif

int main()
  {
#ifdef USE_THREAD
            thread th[ThreadCount];
#endif
   unsigned RowCount=0;
   while(RowCount<1024*1024*1024)
     {
            for(unsigned t=0;t<ThreadCount;++t)
              {
#ifdef USE_THREAD
               th[t]=thread
                 (
                  [t]
#endif
                    {
                     for(unsigned y=t;y<ColumnCount;y+=ThreadCount)
                       {
                        for(unsigned p=0;p<PairCount;++p)
                          {
                           double add=0;
#ifdef USE_DOUBLE
                           double *tcy=TC[p][y];
#else
                           VirtualTab<double> &tcy=TC[p][y];
#endif
                           for(unsigned x=0;x<ColumnCount;++x)
                             {
                              add+=tcy[x]*TR[p][x];
                             }
                           for(unsigned s=0;s<ResultCount;++s)
                             {
                              TY[s][y][p]+=add*(SV[s+DeadCount+ForwardCount].X[p]-SV[s+DeadCount].X[p]);
                             }
                          }
                       }
                    }
#ifdef USE_THREAD
                 );
#endif
              }
            if(!(++RowCount&0xFF))
              {
               cout<<'\r'<<RowCount;
               cout.flush();
              }
#ifdef USE_THREAD
            for(unsigned t=0;t<ThreadCount;++t) th[t].join();
#endif
     }
   return 0;
  }
0

Rozumiem, że odpalasz to też na jakimś Linkusie? Bo Valgrind, który pokazuje Ci bzdury, nie działa na Windowsie AFAIK. Próbowałeś Helgrinda? To część Valgrinda służąca do debugowania wątków właśnie, może będą mniejsze bzudry.

http://valgrind.org/docs/manual/hg-manual.html

Jeżeli nie dotykasz Linuksa to może Thread Sanitizer? Jest w Clangu 3.2 i w GCC 4.8, chyba działa na Windowsie: http://code.google.com/p/thread-sanitizer/wiki/CppManual Swoją drogą jeżeli nie sprawdzałeś tego na Linuksie to może zobacz, czy działa tam (i tam też możesz użyć Valgrinda)? Będzie można stwierdzić, czy to rzeczywiście winpthreads ma problem.

0

Niestety problem jest taki że na linuksie jest cacy (a i pthread przed tym pod linuksem działało bez problemowo) zaś ja muszę to paskudztwo mieć pod windows.

1

Zawsze możesz na tym Linuksie przelecieć toolami żeby mieć 100% pewność, że logika tego kodu jest poprawna. Ja kiedyś wesoło kompilowałem na Linukse (64 bit) i wszystko mi działało, a potem musiałem również uruchomić na Windowsie (32 bit) i oczywiście przestało. I była to moja wina wtedy. Jeżeli jest poprawna i wszystko działa, to zapewne jest jak wspomniałeś już na samym początku - ta implementacja <threada> jest zepsuta. Może spróbuj jakiś inny build MinGW - masz co najmniej trzy do wyboru: MinGW Builds, MinGW-w64 (rubenvb) oraz TDM-GCC (mają "dragon" w adresie). Szczerze mówiąc nie wiem czy są duże różnice między tymi buildami, ale masz do wyboru 32 i 64 bit no i różne wersje samego GCC. A potem to chyba zostaje tylko zgłosić gdzieś buga.

0

Z debugerem to nie bardzo się da, bo to DLL odpalana przez service.

Zrzut robiony podczas crasha wciaz możesz załadować pod debugger.

0

@Dragon:
Udało się rozwiązać problem? Jakiej wersji gcc/mingw używasz?
Skoro działasz na windzie może warto sprawdzić kod pod visual studio, które wspiera std::thread od wersji 2012.
Jak tylko wrócę z pracy to postaram się odpalić kod (chyba jest kompletny, nie?) pod gcc 4.7 i MSVC2013 (tak się składa że mam oba).

0

Jeżeli chodzi o ten testowy to pod linuksem - działa, pod windows'ami VC - działa, pod windows'ami gcc MinGW - tylko wersja eksperymentalna to ma.
Więc zakładam że coś schrzanili i nawet o tym wiedzą (skoro wciąż eksperymentalna) :D
Ostatnio sprawdzany:
i686-w64-mingw32-gcc-4.8-stdthread-win32_rubenvb

1 użytkowników online, w tym zalogowanych: 0, gości: 1