2009-10-13 7 views
1

J'écris une application multithread.High CSwitch ("changement de contexte") lors de l'utilisation du code interprocess Boost (sous Windows, Win32)

j'utilisais les boost :: classes interprocessus (version 1.36.0)

Essentiellement, j'ai threads de travail qui doivent être notifiées lorsque le travail est disponible pour eux de le faire.

J'ai essayé les deux approches "sémaphore" et "condition". Dans les deux cas, le CSwitch (commutateur de contexte) pour les threads de travail semblait très élevé, comme 600 commutations par seconde. J'ai eu un coup d'oeil sur le code et il semble qu'il vérifie juste un drapeau (en utilisant atomiquement un mutex) et puis donne le timeslice avant d'essayer à nouveau la prochaine fois.

Je m'attendais à ce que le code utilise WaitForSingleObject ou quelque chose. Ironiquement, c'était exactement ce que je faisais avant de décider de le faire "correctement" et d'utiliser Boost! (c'est-à-dire en utilisant un mutex pour vérifier régulièrement l'état d'un drapeau). La seule différence était, dans mon approche, je dormais comme 50ms entre les contrôles, donc je n'ai pas eu le problème élevé CSwitch (et oui c'est bien pour moi que le travail ne commencera pas jusqu'à 50ms).

Plusieurs questions:

  1. Est-ce que cette question de valeur cDébranchez "haute"? Cela se produirait-il si la bibliothèque d'amplification utilisait CRITICAL_SECTIONS au lieu de sémaphores (je ne me soucie pas de la synchronisation inter-processus - tous les threads sont dans le même processus)?
  2. Est-ce que cela se produirait si boost utilisait WaitForSingleObject?
  3. Y a-t-il une autre approche dans les bibliothèques Boost qui utilise les méthodes d'attente Win32 mentionnées ci-dessus (WaitForXXX) qui, je suppose, ne souffriront pas de ce problème CSwitch.

Mise à jour: Voici un exemple de pseudo-code. Je ne peux pas ajouter le vrai code parce que ce serait un peu complexe. Mais c'est à peu près ce que je fais. Cela commence juste un fil pour faire une activité asynchrone unique.

REMARQUE: Ce ne sont que des illustrations! Il y a des charges manquantes dans cet échantillon, par ex. Si vous appelez injectWork() avant que le thread n'ait atteint "wait", cela ne fonctionnera pas. Je voulais juste illustrer mon utilisation du boost.

L'utilisation est quelque chose comme:

int main(int argc, char** args) 
{ 
    MyWorkerThread thread; 
    thread.startThread(); 

    ... 

    thread.injectWork("hello world"); 
} 

Voici l'exemple en utilisant boost.

class MyWorkerThread 
{ 
public: 

    /// Do work asynchronously 
    void injectWork(string blah) 
    { 
    this->blah = blah; 

    // Notify semaphore 
    this->semaphore->post(); 
    } 

    void startThread() 
    { 
    // Start the thread (Pseudo code) 
    CreateThread(threadHelper, this, ...); 
    } 

private: 


    static void threadHelper(void* param) 
    { 
    ((MyWorkerThread*)param)->thread(); 
    } 

    /// The thread method 
    void thread() 
    { 
    // Wait for semaphore to be invoked 
    semaphore->wait(); 

    cout << blah << endl; 
    } 

    string blah; 
    boost::interprocess::interprocess_semaphore* semaphore; 
}; 

Et voici mon code de vote "naïf":

class MyWorkerThread_NaivePolling 
{ 
public: 

    MyWorkerThread_NaivePolling() 
    { 
    workReady = false; 
    } 

    /// Do work asynchronously 
    void injectWork(string blah) 
    { 
    section.lock(); 

    this->blah = blah; 
    this->workReady = true; 

    section.unlock(); 
    } 

    void startThread() 
    { 
    // Start the thread (Pseudo code) 
    CreateThread(threadHelper, this, ...); 
    } 

private: 

    /// Uses Win32 CriticalSection 
    class MyCriticalSection 
    { 
    MyCriticalSection(); 
    void lock(); 
    void unlock(); 
    }; 

    MyCriticalSection section; 


    static void threadHelper(void* param) 
    { 
    ((MyWorkerThread*)param)->thread(); 
    } 

    /// The thread method 
    void thread() 
    { 
    while (true) 
    { 
     bool myWorkReady = false; 
     string myBlah; 

     // See if work set 
     section.lock(); 
     if (this->workReady) 
     { 
     myWorkReady = true; 
     myBlah = this->blah; 
     } 
     section.unlock(); 

     if (myWorkReady) 
     { 
     cout << blah << endl; 
     return; 
     } 
     else 
     { 
     // No work so sleep for a while 
     Sleep(50); 
     } 
    } 
    } 

    string blah; 
    bool workReady; 
}; 

Cheers,

John

+0

Pourriez-vous afficher le code que vous utilisez dans les threads producteur et consommateur (pour les deux approches)? –

+0

Yup - code d'échantillon ajouté. – John

Répondre

0

Si cela ne vous dérange pas spécifique à Windows (versions plus récentes sur les fenêtres), vérifiez le lien pour light weight condition variables CONDITION_VARIABLE (comme les sections critiques):

+0

Merci pour votre suggestion. Malheureusement, je dois supporter XP. De plus, j'essaie de garder le code de base portable autant que possible. Merci, cependant. – John

3

Sur un système non-POSIX ems, il semble que interprocess_condition est émulé en utilisant une boucle, comme vous le décrivez dans votre question. Et interprocess_semaphore est émulé avec un mutex et un interprocess_condition, alors wait() - finit dans la même boucle.

Puisque vous mentionnez que vous n'avez pas besoin de la synchronisation interprocess, vous devriez regarder Boost.Thread, qui offre une implémentation portable de condition variables. Amusant, il semble qu'il est mis en œuvre sur Windows de la manière "classical", en utilisant un ... Sémaphore.

+0

C'est génial - je vais essayer ça. – John

Questions connexes