2010-09-17 6 views
2

Voici le problème que j'ai avec le multithreading. Le processus doit être statique, ce qui signifie que la seule façon dont je vois que 2 threads peuvent communiquer et partager des données est à travers la portée globale. Cela ne semble pas très propre et ne se sent pas très OO. Je sais que je peux créer une fonction de processus statique dans une classe, mais cela reste statique. Ce que je voudrais faire, par exemple, est d'avoir des threads threads dans la classe pour que je puisse créer une classe de checksum MD5 et avoir un tableau de ces objets, chacun sur son propre thread vérifiant son hash, alors que le fil de l'interface utilisateur n'est pas affecté par cela et une autre classe pourrait simplement garder une trace des poignées et attendre plusieurs objets avant de dire "Complete" ou quelque chose. Comment cette limitation est-elle habituellement surmontée?multithreading et classes?

+0

Pourriez-vous être plus précis sur votre déclaration et ensuite dans votre question?C'est tellement vague que j'ai du mal à comprendre quelles sont les hypothèses sur le filetage que vous faites (les fils BTW existent en plusieurs versions, selon l'OS), et encore moins ce que vous voulez vraiment savoir. Pourquoi est-ce une question C? –

+0

De nombreux outils de communication inter-processus (IPC) peuvent également être utilisés entre les threads. Pipes, douilles, fichiers. Chacun vient avec des coûts et des avantages. Il est temps de faire quelques lectures ... – dmckee

+0

J'ai enlevé la balise C car la confusion dans les réponses montre que 'static' dans cette question signifie simplement' static' fonction membre dans le sens de C++ et pas un symbole 'static' qui confondre tous les gars à la recherche de C. –

Répondre

3

Vous ne pouvez pas éviter d'utiliser une fonction statique si vous souhaitez y démarrer un thread. Vous pouvez cependant (en utilisant Windows) passer le pointeur this comme paramètre et l'utiliser de l'autre côté pour entrer dans l'instance de classe.

#include <windows.h> 

class Threaded { 
    static DWORD WINAPI StaticThreadEntry(LPVOID me) { 
     reinterpret_cast<Threaded*>(me)->ThreadEntry(); 
     return 0; 
    } 

    void ThreadEntry() { 
     // Stuff here. 
    } 

public: 
    void DoSomething() { 
     ::CreateThread(0, 0, StaticThreadEntry, this, 0, 0); 
    } 
}; 
+1

vous pouvez éviter d'utiliser une fonction statique si vous utilisez Boost (que vous devriez si vous programmez en C++) – jalf

+0

Un trampoline statique est certainement le chemin à parcourir. La solution * nix utilisant pthread_create() est analogue. – Boojum

+1

@jalf Parfois, Boost n'est tout simplement pas disponible ou ajoute une forte dépendance dans un projet qui n'est pas souhaitable. –

3

En C++, Boost.Thread résout bien le problème. Un thread est représenté par un foncteur, ce qui signifie que le operator() (non statique) est le point d'entrée du thread.

Par exemple, un fil peut être créé comme ceci:

// define the thread functor 
struct MyThread { 
    MyThread(int& i) : i(i) {} 
    void operator()(){...} 
private: 
    int& i; 
}; 

// create the thread 
int j; 
boost::thread thr(MyThread(j)); 

en transmettant les données au constructeur du foncteur de fil, on peut passer des paramètres au fil sans avoir à compter sur globals. (Dans ce cas, le thread reçoit une référence à l'entier j déclaré en dehors du thread.)

Avec d'autres bibliothèques ou API, c'est à vous de faire le saut d'un point d'entrée (généralement statique) au partage non -statique des données.

La fonction thread prend généralement un paramètre (parfois facultatif) (souvent de type void*) que vous pouvez utiliser pour transmettre des données d'instance au thread.

Si vous utilisez cette commande pour passer un pointeur vers un objet, le thread peut simplement renvoyer le pointeur vers le type d'objet et accéder aux données sans avoir à s'appuyer sur les globales.

Par exemple, (en pseudocode), cela aurait à peu près le même effet que l'exemple Boost ci-dessus:

void MyThreadFunc(void* params) { 
    int& i = *(int*)params; 
    ... 
} 

int j; 
CreateThread(MyThreadFunc, &j); 

Ou le paramètre peut être un pointeur vers un objet dont (non statique) fonction membre vous souhaitez appeler, ce qui vous permet d'exécuter une fonction de membre de classe au lieu d'un non-membre.

+0

Pour moi, cela ne résout pas le problème. C'est exactement le même problème emballé différemment, parce que les données d'instance sont toujours transmises par l'intermédiaire de void *. Quel est le bénéfice? Tout ce qui arrive est que vous avez ajouté une autre classe à la hiérarchie. C'est une bonne solution de portabilité, mais cela n'a pas grand chose à voir avec la question. Ou ai-je oublié quelque chose qui vous permet d'instancier le fil et de fonctionner directement sur les données? –

+0

@San Jacinto: le PO a supposé que la seule solution est de partager les données allouées dans le cadre mondial. Un 'void *' passé en paramètre au thread n'est pas global, donc il résout le problème de l'OP. De plus, la solution Boost préserve également la sécurité du type. – jalf

+0

oui, je vois ce que vous voulez dire maintenant après avoir relu le message :) –

0

Les routines de création de threads permettent généralement de passer un paramètre à la fonction qui sera exécutée dans un nouveau thread. Cela est vrai pour Posix pthread_create (...) et Win32 CreateThread (...). Voici un exemple utilisant Pthreads:

void* func (void* arg) { 
    queue_t* pqueue = (queue_t*)arg; 

    // pull messages off the queue 
    val = queue_pull(pqueue); 

    return 0; 
} 

int main (int argc, char* argv[]) { 
    pthread_t thread; 
    queue_t queue = queue_init(); 

    pthread_create(&thread, 0, func, &queue); 

    // push messages on the queue for the thread to process 
    queue_push(&queue, 123); 

    void* ignored; 
    pthread_join(&thread, &ignored); 

    return 0; 
} 

Aucune statique nulle part. Dans un programme C++, vous pouvez passer un pointeur sur une instance d'une classe.

+0

Ceci est vrai, mais vous êtes en train de polluer l'espace de noms (dans votre cas, l'espace de noms global). C'est pourquoi une fonction membre statique est préférée. En utilisant un, vous avez un mécanisme égal à ce que vous décrivez sans les problèmes de namspace qu'il crée. –

+0

Si vous aimez créer des classes pour tout, c'est bien. En C++, vous pouvez facilement envelopper la fonction de thread dans un espace de noms si c'est votre sac. À mon goût, c'est six ou six douzaines. – xscott