2010-05-23 3 views
2

J'essaie de créer une classe pour la programmation réseau. Cela va créer une socket à usage général avec thread.Créer un thread n'accepte pas la fonction membre

Mais quand j'ai essayé de créer le fil en utilisant createthread(). Le troisième argument est la production d'erreurs. Et à partir du net, j'ai appris que je ne pouvais pas utiliser les fonctions membres comme argument à createthread().

Y at-il une chose par laquelle je peux y parvenir?

+0

Quelle langue? – kennytm

+0

J'utilise vC++ – prabhakaran

+0

Le problème est qu'une "fonction membre" est en fait deux choses: un pointeur vers la fonction, et un pointeur vers l'instance à laquelle l'appliquer. C++ ne peut pas transformer cela en un simple pointeur de fonction (enfin, ça peut, mais ça ne marche pas assez fort). –

Répondre

-1

A perdu, je l'ai eu, le fait est, dans CreateThread si vous passez la prise alors il n'y a aucun problème. Parce que CreateThread est en prenant soin de ce prise. Mais si vous passez comme un objet qui est d'avoir que prise, puis CreateThread est ne pas prendre soin de la prise , et il est finit dans prise invalide dans le nouveau thread.

Le code successed ci-dessous

SOCKET s=socket(....); 
bind(s,...); 
listen(s,...); 
SOCKET temp=accept(s,(sockaddr *)&addrNew,&size); 
DWORD threadId; 
HANDLE thread=CreateThread(NULL,0,&MyThreadFunction,(LPVOID)(temp),0,&threadId); 
+2

Il n'y a aucune raison que le passage d'un pointeur d'objet contenant le socket en tant que variable membre ne fonctionne pas. Si la variable socket est devenue invalide, cela est dû à une erreur de codage de votre part. CreateThread n'a aucun concept de sockets, ou comment "prendre soin" d'eux. –

+0

Je ne pense pas. J'examine ceci pendant presque deux semaines. Tu peux le vérifier. Mon hypothèse est que le socket a besoin d'un environnement, et quand vous le créez via CreateThread, createthread passe aussi l'environnement. Mais si vous passez un objet (qui a cette socket), alors dans le thread, il est invalide. Je l'ai vérifié. Et MSDN forum montrant également des exemples comme socket de passage dans createthread. Il semble qu'ils cachent un point de créature moins. – prabhakaran

+0

L'explication est complètement incorrecte; Voir la réponse de Brook Miles. -1 pour ça. –

16

La façon la plus simple de gérer cela est de créer une fonction "stub" qui rappelle dans votre classe.

UINT tid 
HANDLE hThread = CreateThread(NULL, 0, myThreadStub, this, 0, &tid); 

.... 

unsigned long WINAPI myThreadStub(void *ptr) 
{ 
    if (!ptr) return -1; 
    return ((MyClass*)ptr)->ThreadMain(); 
} 

CreateThread() vous permet de passer un argument à la fonction de fil (paramètre 4 de l'appel CreateThread()). Vous pouvez l'utiliser pour passer un pointeur sur votre classe. Vous pouvez ensuite renvoyer le pointeur vers le type approprié, puis appeler une fonction membre. Vous pouvez même avoir "myThreadStub" être un statique membre de "MyClass", lui permettant d'accéder à des membres privés et des données.

Si vous avez installé boost, vous pouvez utiliser boost :: bind pour cela sans créer de fonction de remplacement. Je ne l'ai jamais essayé sur les fenêtres, je ne peux pas dire pour sûr que cela fonctionne (parce que la fonction de rappel doit être un appel WINAPI) mais si cela ne fonctionne, il ressemblerait à quelque chose comme:

HANDLE hThread = CreateThread(NULL, 0, boost::bind(&MyClass::ThreadFunction, this), NULL, 0, &tid); 

Où La fonction thread est une fonction membre non statique qui prend un seul argument void *.

+0

pouvez-vous s'il vous plaît dire comment afficher le code comme ci-dessus – prabhakaran

+0

Utilisez le bouton "1010", ou tout simplement indenter 4 espaces. – SoapBox

+2

Boost.Bind n'aide pas ici - il renvoie des foncteurs, pas des pointeurs de fonction. –

3

Il y a un moyen facile de résoudre le problème.

Jetez un oeil à ThreadProc callback function:

 

    DWORD WINAPI ThreadProc(
     __in LPVOID lpParameter 
    ); 

Et maintenant CreateThread function:

 

    HANDLE WINAPI CreateThread(
     __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, 
     __in  SIZE_T dwStackSize, 
     __in  LPTHREAD_START_ROUTINE lpStartAddress, 
     __in_opt LPVOID lpParameter, 
     __in  DWORD dwCreationFlags, 
     __out_opt LPDWORD lpThreadId 
    ); 

Utilisez une méthode statique procédure de fil, mais appeler à partir d'une méthode membre, et passer le pointeur d'objet à ce:

 

    #include <windows.h> 

    class MyClass { 
    public: 
     void CreateThreadForObject() { 
     LPSECURITY_ATTRIBUTES security_attributes = 0; 
     SIZE_T stack_size = 0; 
     LPTHREAD_START_ROUTINE start_routine = &MyClass::ThreadProcForObject; 
     LPVOID param = this; 
     DWORD creation_flags = 0; 
     LPDWORD thread_id = 0; 
     CreateThread(security_attributes, stack_size, start_routine, param, 
        creation_flags, thread_id); 
     } 

    private: 
     static DWORD WINAPI ThreadProcForObject(LPVOID param) { 
     MyClass* instance = reinterpret_cast<MyClass*>(param); 
     if (!instance) return 1; 
     // ... 
     return 0; 
     } 
    }; 

Désolé, je n'ai juste pas assez de temps pour w Rite un bon exemple. Mais je pense que vous comprenez le chemin.

+2

Je marquerais ce +10. C'est la première méthode que j'ai trouvée qui ne révèle pas les internes de classe au public. Je suis C# dev et C# fait des choses amusantes pour moi. –

+0

peut-être vous si savoir, peut faire la même chose avec lambda? –

Questions connexes