2011-07-05 8 views
12

Czy std :: random_shuffle threadsafe? Nie sądzę, że zwykły rand() nie jest bezpieczny dla wątków. Jeśli tak jest, w jaki sposób mogę użyć rand_r z random_shuffle, aby móc nadać każdemu wątkowi unikalne ziarno. Widziałem przykłady używania niestandardowych generatorów losowych z random_shuffle, ale wciąż nie jest to dla mnie jasne.Czy random_shuffle threadsafe? i używanie rand_r jeśli nie jest

Dzięki.

+4

Generalnie trzeba zrobić założenie, że * nie * w bibliotece C++ to Wątek bezpieczny, chyba że dokumentacja stanowi inaczej. –

+1

Również "Threadafe" to termin bardzo przeciążony. Niektóre algorytmy są bezpieczne tylko wtedy, gdy działają na bezpiecznych danych. Niektóre są bezpieczne dla wszystkich wątków, o ile istnieje tylko 1 scenarzysta, a większość nie może tego zagwarantować. Zasadniczo przy podejmowaniu decyzji o tym, co jest bezpieczne (tj. Poprawne), wymagane jest określenie różnych wymagań odczytu/zapisu. – Kylotan

+0

Aby wyjaśnić, chcę równolegle tasować różne listy.Nie martwię się więc o wyścigi w strukturze danych, tylko generowanie losowych liczb dla shuffle. – Mark

Odpowiedz

4

Aby korzystać rand_r z std::random_shuffle, musisz napisać dość trywialne (opakowanie). Generator liczb losowych, który przeszedłeś pod numer random_shuffle, musi zaakceptować parametr, który określa zakres liczb, które mają zostać wyprodukowane, a które nie są dostępne.

Twój wrapper będzie wyglądać mniej więcej tak:

class rand_x { 
    unsigned int seed; 
public: 
    rand_x(int init) : seed(init) {} 

    int operator()(int limit) { 
     int divisor = RAND_MAX/(limit+1); 
     int retval; 

     do { 
      retval = rand_r(&seed)/divisor; 
     } while (retval > limit); 

     return retval; 
    }   
}; 

Można by używać go z random_shuffle coś takiego:

std::random_shuffle(whatever.begin(), whatever.end(), rand_x(some_seed)); 
+0

Dzięki. Seed powinien być unsigned int. Ponadto, dlaczego istnieje pętla do wykonania, a nie powracający limit% rand_r (& seed)? Czy jest coś subtelnego, czego mi brakuje? – Mark

+0

@ Mark: Ups - naprawione. Jeśli chodzi o pętlę do zrobienia, zastanów się, jak dzielisz 10 cukierków równo pomiędzy 3 dzieci (i nie możesz podzielić jednego cukierka na kawałki). Odpowiedź brzmi: nie możesz - możesz rozpowszechniać tylko 9 z nich. Zasadniczo robi się to samo dla dzielenia cukierków "RAND_MAX" pomiędzy dziećmi "limit" i odrzucania wszelkich resztek, aby wszystkie stosy były równe. Użycie '% limitu' (lub'/divisor') samo w sobie * nie może * dzielić liczb równomiernie, chyba że 'RAND_MAX' stanie się dokładną wielokrotnością' limitu' (a 'RAND_MAX' jest często liczbą pierwszą, więc nie jest to dokładna wielokrotność * dowolnego * znaczącego 'limitu'). –

+0

Chociaż zgadzam się ze wszystkim, co powiedział Jerry, można argumentować, że jeśli używasz 'rand_r', to nie masz prawa zakładać, że ma on jednolitą dystrybucję do zachowania. Ale przynajmniej w ten sposób, * jeśli * 'rand_r' jest dobre, twoje tasowanie też jest dobre, nie wprowadzasz żadnej nowej stronniczości. –

3

Musisz podać funkcję generatora liczb losowych lub obiektu funktora, który przyjmuje typ wartości integralnej i zwraca inną wartość jakiegoś typu integralnego, która nie przepełni granic kontenera, że ​​iteratory, które przekazałeś do funkcji, są iterowanie przez. Również w przypadku obiektu functor musi on implementować operator(), aby można było go nazwać jak funkcję. Ponieważ potrzebujesz generatora liczb losowych bezpiecznych dla wątków, używanie srand iz cstdlib jest złym pomysłem ... powinieneś zamiast tego utworzyć obiekt funktorowy, który implementuje generator liczb losowych bezpieczny dla wątków lub generator liczb losowych, który nie implementuje globalnie dostępnych zmiennych, dzięki czemu wszystko pozostaje w pamięci lokalnej.

Na przykład, jednym ze sposobów, w jaki może to działać, jest generator liczb losowych, który uzyskałeś z innej biblioteki, która generuje tylko wartości losowe między ustalonym zakresem wartości, dzięki czemu możesz zdefiniować granice kontenera dla iteratorów o dostępie swobodnym używany jest algorytm random_shuffle. Teraz w zależności od tego, co biblioteka użyć, można funktor wyglądać mniej więcej tak:

class my_rand_gen 
{ 
    private: 
     random_gen_type random_range_gen; 
     int min; 
     int max; 

    public: 
     my_rand_gen(const random_gen_type& gen, int min_range, int max_range): 
        random_range_gen(gen), min(min_range), max(max_range) {} 

     int operator()(int value) 
     { 
      //ignore the input value and use our own defined range 
      //returns a value between min and max 
      return random_range_gen(min, max); 
     } 
}; 

teraz można nazwać algorytmu takiego:

random_shuffle(my_vector_start_iter, my_vector_end_iter, 
       my_rand_gen(rand_generator_lib, 
          vector_start_index, 
          vector_end_index)); 

i będzie przetasować wektor w między początkiem i kończ iteratory do twojego wektora bez przepełnienia granic wektora ... innymi słowy użyje tylko wartości dla przetasowania pomiędzy vector_start_index i vector_end_index.

+0

Nowe klasy' 'byłyby dobrym początkiem - możesz mieć jeden PRNG na wątek. –

Powiązane problemy