Charge CPU Massive en utilisant std:: lock (c++11)

mes récents efforts pour implémenter un gestionnaire thread/ mutex se sont soldés par une charge CPU de 75% (4 core), alors que les quatre threads en cours d'exécution étaient soit en sommeil, soit en attente d'un mutex déverrouillé.

la classe spécifique est beaucoup trop grande pour être affiché ici entièrement, mais je pourrais réduire la cause à l'impasse-acquisition sécuritaire de deux mutex

std::unique_lock<std::mutex> lock1( mutex1, std::defer_lock );
std::unique_lock<std::mutex> lock2( mutex2, std::defer_lock );
std::lock( lock1, lock2 );

une Autre partie de la classe utilise un std::condition_variable avec wait() et notify_one() sur mutex1 pour un peu de code pour être exécuté de manière sélective dans le même temps.

Le simple fait de changer de

std::unique_lock<std::mutex> lock1( mutex1 );
std::unique_lock<std::mutex> lock2( mutex2 );

a ramené L'utilisation CPU à la normale 1-2%.

Je ne peux pas croire, la fonction std::lock() est aussi inefficace. Cela pourrait-il être un bug dans g++ 4.6.3?

edit: ( exemple )

#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
#include <condition_variable>

std::mutex mutex1, mutex2;
std::condition_variable cond_var;

bool cond = false, done = false;

using namespace std::chrono_literals;

void Take_Locks()
    {
    while( !done )
        {
        std::this_thread::sleep_for( 1s );

        std::unique_lock<std::mutex> lock1( mutex1, std::defer_lock );
        std::unique_lock<std::mutex> lock2( mutex2, std::defer_lock );
        std::lock( lock1, lock2 );

        std::this_thread::sleep_for( 1s );
        lock1.unlock();
        lock2.unlock();
        }
    }

void Conditional_Code()
    {
    std::unique_lock<std::mutex> lock1( mutex1, std::defer_lock );
    std::unique_lock<std::mutex> lock2( mutex2, std::defer_lock );

    std::lock( lock1, lock2 );
    std::cout << "t4: waiting n";

    while( !cond )
        cond_var.wait( lock1 );

    std::cout << "t4: condition met n";
    }

int main()
    {
    std::thread t1( Take_Locks ), t2( Take_Locks ), t3( Take_Locks );
    std::thread t4( Conditional_Code );

    std::cout << "threads started n";
    std::this_thread::sleep_for( 10s );

    std::unique_lock<std::mutex> lock1( mutex1 );
    std::cout << "mutex1 locked n" ;
    std::this_thread::sleep_for( 5s );

    std::cout << "setting condition/notify n";
    cond = true;
    cond_var.notify_one();
    std::this_thread::sleep_for( 5s );

    lock1.unlock();
    std::cout << "mutex1 unlocked n";
    std::this_thread::sleep_for( 6s );

    done = true;
    t4.join(); t3.join(); t2.join(); t1.join();
    }
25
demandé sur Innocent Bystander 2012-12-02 12:59:32

4 réponses

sur ma machine, le code suivant imprime 10 fois par seconde et consomme presque 0 cpu parce que la plupart du temps le fil est soit endormi ou bloqué sur un mutex verrouillé:

#include <chrono>
#include <thread>
#include <mutex>
#include <iostream>

using namespace std::chrono_literals;

std::mutex m1;
std::mutex m2;

void
f1()
{
    while (true)
    {
        std::unique_lock<std::mutex> l1(m1, std::defer_lock);
        std::unique_lock<std::mutex> l2(m2, std::defer_lock);
        std::lock(l1, l2);
        std::cout << "f1 has the two locks\n";
        std::this_thread::sleep_for(100ms);
    }
}

void
f2()
{
    while (true)
    {
        std::unique_lock<std::mutex> l2(m2, std::defer_lock);
        std::unique_lock<std::mutex> l1(m1, std::defer_lock);
        std::lock(l2, l1);
        std::cout << "f2 has the two locks\n";
        std::this_thread::sleep_for(100ms);
    }
}

int main()
{
    std::thread t1(f1);
    std::thread t2(f2);
    t1.join();
    t2.join();
}

sortie de L'échantillon:

f1 has the two locks
f2 has the two locks
f1 has the two locks
...

j'exécute ceci sur OS X et L'application de moniteur D'activité dit que ce processus utilise 0.1% cpu. La machine est un Intel Core i5 (4 core).

je suis heureux d'ajuster cette expérience en tout moyen de tenter de créer un live-lock ou une utilisation excessive du cpu.

mise à Jour

si ce programme utilise un CPU excessif sur votre plate-forme, essayez de le changer pour appeler ::lock() à la place, où cela est défini par:

template <class L0, class L1>
void
lock(L0& l0, L1& l1)
{
    while (true)
    {
        {
            std::unique_lock<L0> u0(l0);
            if (l1.try_lock())
            {
                u0.release();
                break;
            }
        }
        std::this_thread::yield();
        {
            std::unique_lock<L1> u1(l1);
            if (l0.try_lock())
            {
                u1.release();
                break;
            }
        }
        std::this_thread::yield();
    }
}

je serais intéressé de savoir si cela a fait une différence pour vous, merci.

Update 2

après un long retard, j'ai écrit une première ébauche d'un document à ce sujet. Le document compare quatre façons différentes de faire ce travail. Il contient un logiciel que vous pouvez copier et coller dans votre propre code et tester vous-même (et s'il vous plaît rapporter ce que vous trouvez!):

http://howardhinnant.github.io/dining_philosophers.html

23
répondu Howard Hinnant 2018-08-23 22:12:16

Le std::lock() non-membre de la fonction peut cause Live-problème de verrouillage ou de dégradation des performances, il garantit seulement " Never Dead-lock ".

si vous pouvez déterminer" ordre de serrure(hiérarchie de serrure) "de plusieurs mutex par conception, il est préférable de ne pas utiliser le std::lock() générique mais de verrouiller chaque mutex dans un ordre prédéterminé.

Consultez l'Acquisition de Plusieurs Serrures Sans Blocage pour plus de détails.

6
répondu yohjp 2013-01-25 03:18:54

, Comme le dit la documentation, [l]es objets sont verrouillés par un quelconque série d'appels à verrouiller, try_lock, déverrouiller . Il n'y a tout simplement aucun moyen qui peut éventuellement être efficace si les mutex sont tenus par d'autres threads pour une période de temps significative. Il n'y a aucun moyen que la fonction puisse attendre sans tourner.

4
répondu David Schwartz 2012-12-02 09:14:07

je tiens D'abord à remercier pour toutes les réponses.

pendant le travail sur un exemple de code, qui reproduit l'effet, j'ai trouvé la source du problème.

la partie conditionnelle bloque les deux mutex, alors qu'elle n'en utilise qu'un pour la fonction std::condition_variable::wait() .

mais je me demande toujours, ce qui se passe derrière la scène, qui produit une charge CPU si élevée.

0
répondu mac 2012-12-03 04:09:17