2010-07-28 4 views
1

Je souhaite exécuter plusieurs threads à l'intérieur d'un processus. Je suis à la recherche du moyen le plus efficace de transmettre des messages entre les threads.Recherche d'une file d'attente de messages multithread optimale

Chaque thread aurait un tampon de message d'entrée de mémoire partagée. D'autres threads écriraient le tampon approprié.

Les messages auraient la priorité. Je veux gérer ce processus moi-même. Sans entrer dans un verrouillage ou une synchronisation onéreux, quelle est la meilleure façon de procéder? Ou existe-t-il déjà une bibliothèque bien éprouvée disponible pour cela? (Delphi, C ou C# est bien).

Répondre

2

Il est difficile d'obtenir le droit sans répéter beaucoup d'erreurs d'autres personnes déjà fait pour vous :)

Jetez un oeil à Intel Threading Building Blocks - la bibliothèque dispose de plusieurs modèles de file d'attente bien conçus (et d'autres collections) que vous peut tester et voir ce qui convient le mieux à votre objectif.

+0

Merci Nikolaï :) – IamIC

1

Si vous envisagez d'utiliser plusieurs threads, il est difficile d'éviter la synchronisation. Heureusement, ce n'est pas très dur.

Pour un seul processus, une section critique est souvent le meilleur choix. C'est rapide et facile à utiliser. Pour la simplicité, je l'enveloppe normalement dans une classe pour gérer l'initialisation et le nettoyage.

#include <Windows.h> 

class CTkCritSec 
{ 
public: 
    CTkCritSec(void) 
    { 
     ::InitializeCriticalSection(&m_critSec); 
    } 
    ~CTkCritSec(void) 
    { 
     ::DeleteCriticalSection(&m_critSec); 
    } 
    void Lock() 
    { 
     ::EnterCriticalSection(&m_critSec); 
    } 
    void Unlock() 
    { 
     ::LeaveCriticalSection(&m_critSec); 
    } 

private: 
    CRITICAL_SECTION m_critSec; 
}; 

Vous pouvez encore plus simple faire en utilisant une classe « verrouillage automatique » vous verrouillez/déverrouiller.

class CTkAutoLock 
{ 
public: 
    CTkAutoLock(CTkCritSec &lock) 
    : m_lock(lock) 
    { 
     m_lock.Lock(); 
    } 
    virtual ~CTkAutoLock() 
    { 
     m_lock.Unlock(); 
    } 
private: 
    CTkCritSec &m_lock; 
}; 

Partout où vous voulez verrouiller quelque chose, instanciez un autolock. Lorsque la fonction se termine, elle se déverrouille. En outre, s'il y a une exception, il se déverrouillera automatiquement (donnant une sécurité d'exception).

Maintenant, vous pouvez faire une file d'attente simple message d'une file d'attente prioritaire std

#include <queue> 
#include <deque> 
#include <functional> 
#include <string> 

struct CMsg 
{ 
    CMsg(const std::string &s, int n=1) 
    : sText(s), nPriority(n) 
    { 
    } 
    int   nPriority; 
    std::string sText; 

    struct Compare : public std::binary_function<bool, const CMsg *, const CMsg *> 
    { 
     bool operator() (const CMsg *p0, const CMsg *p1) 
     { 
      return p0->nPriority < p1->nPriority; 
     } 
    }; 
}; 

class CMsgQueue : 
     private std::priority_queue<CMsg *, std::deque<CMsg *>, CMsg::Compare > 
{ 
public: 
    void Push(CMsg *pJob) 
    { 
     CTkAutoLock lk(m_critSec); 
     push(pJob); 
    } 
    CMsg *Pop() 
    { 
     CTkAutoLock lk(m_critSec); 

     CMsg *pJob(NULL); 
     if (!Empty()) 
     { 
      pJob = top(); 
      pop(); 
     } 
     return pJob; 
    } 
    bool Empty() 
    { 
     CTkAutoLock lk(m_critSec); 

     return empty(); 
    } 

private: 
    CTkCritSec m_critSec; 
}; 

Le contenu de cmsg peut être tout ce que vous aimez. Notez que le CMsgQue hérite en privé de std :: priority_queue. Cela empêche l'accès brut à la file d'attente sans passer par nos méthodes (synchronisées).

Attribuez une file d'attente comme celle-ci à chaque thread et vous êtes sur votre chemin.

Clause de non-responsabilité Le code ici a été collé rapidement pour illustrer un point. Il a probablement des erreurs et doit être examiné et testé avant d'être utilisé en production.

+0

Merci Michael! J'ai vu une solution utilisant ASM qui élimine les besoins de verrous! Je ne pense pas que ce soit possible dans .Net. – IamIC

+0

@IanC Je ne vois pas comment vous pouvez le faire sans verrous. Je suppose que le code ASM fait les verrous lui-même. Si deux threads tentent d'accéder à une file d'attente simultanément, sans verrous, de mauvaises choses se produisent. :-(Vous pouvez faire votre propre verrouillage, en utilisant les fonctions Interlocked *(), mais généralement ce n'est pas plus simple que d'utiliser une section critique –

+0

J'ai remarqué que sur ma machine, je peux verrouiller et déverrouiller 2 millions de fois par seconde. Mais en utilisant les fonctions ASM atomiques, je peux effectuer plus de 20 millions de verrous/déverrouiller une seconde (en utilisant cmpxchg) .Il doit donc y avoir une différence significative entre les deux méthodes. – IamIC

Questions connexes