2010-09-23 4 views
4

Je suis censé implémenter une bibliothèque de threads userlevel en C. Pour ce faire, j'ai besoin de mettre en œuvre les fonctions yield(), createThread() et destroyThread(). Je crois que j'ai les bonnes bases:Threads dans l'espace utilisateur et le rendement

Pour garder une trace de fils, je vais utiliser une file d'attente d'ThreadControlBlock éléments (qui ressemblent à PCBs dans un OS) qui ressemblent à ceci:

struct ThreadControlBlock { 
    int ThreadId, 
    u_context *context }; 

Nous peut utiliser la famille de fonctions setcontext() pour "sauvegarder" et "charger" le contexte. Lors de l'initialisation du programme, initialisez ThreadQueue sans éléments.

Maintenant, la partie que je ne reçois pas: quand un thread appelle yield(), j'obtiens le contexte actuel et l'enregistre dans un ThreadControlBlock et mettre dans la file d'attente. Ensuite, récupérez le premier élément dans la file d'attente et chargez le contexte, puis l'exécution se poursuit.

La question est, si je fais cela, dites que je suis un fil appelant yield() et le prochain fil est moi-même. Si je sauve le contexte et que je le charge à nouveau, je ne serais pas au même endroit où je me trouvais (avant d'appeler le yield()?) Et cela continuerait toujours?

+0

+1 pour une question connexe exemplaire à un devoir qui n'est pas seulement "s'il vous plaît faire mes devoirs pour moi". C'est une question intéressante en elle-même, une question qui touche au cœur du fonctionnement de la commutation de contexte de tâche. – RBerteig

Répondre

0

Le même problème s'applique en fait si vous passez à une autre tâche, puisque cette autre tâche a enregistré son contexte au même point (où il était sur le point de passer à une seconde tâche). En utilisant setcontext() et getcontext(), vous devez utiliser une variable statique pour garder la trace si vous passez ou déconnectant:

static volatile int switched; 

switched = 0; 
getcontext(current->context); 
if (!switched) 
{ 
    switched = 1; 
    setcontext(next->context); 
} 

Alternativement, vous pouvez simplement utiliser swapcontext(current->context, next->context);

0

Il est parfaitement raisonnable dans votre implémentation yield() de vérifier si le thread suivant est le thread en cours et de traiter ce cas comme un no-op.

+0

On nous a dit de ne pas le faire ... – Aillyn

+0

@pessimopoppotamus: s'il n'y a pas d'autres threads, alors vous devez le faire.Exemple amusant des planificateurs de tâches: aucun d'eux ne supporte 0 tâches. C'est pourquoi Windows a "System Idle Task" et * NIX a le "init". Donc, le planificateur a toujours quelque chose à planifier. 'yield()' comme un appel à scheduler, est essentiellement un moyen de permettre [multi-tâches coopératives] (http://en.wikipedia.org/wiki/Computer_multitasking#Cooperative_multitasking.2Ftime-sharing). S'il n'y a personne à "coopérer" avec, "yield()" ne peut rien faire. – Dummy00001

1

Lorsqu'un thread appelle yield(), vous devez enregistrer l'état d'un thread sur le point de revenir d'un appel yield(). Ne sauvegardez pas le contexte immédiatement avant le yield().

0

S'il n'y a pas d'autres threads à exécuter en dehors du thread actuel, il n'y a rien d'autre à faire que de revenir simplement du rendement. Je ne voudrais pas appeler swapcontext dans ce cas, cependant - juste détecter et retourner.

Je pense que ce que vous avez réellement à faire est ce qu'il faut faire quand aucun thread (y compris le courant) lorsque le rendement est appelé. Une façon simple de gérer cela est d'avoir un thread inactif, qui n'est exécuté que lorsque la file d'attente d'exécution (threads prêts) est vide. Ce fil sera probablement:

{ 
     while (1) { 
      yield(); 
      pause(); 
     } 
    } 

Cela permet à votre programme d'aller dormir (via pause) jusqu'à ce qu'un signal arrive. Espérons que le signal sera un événement qui rendra l'un des autres threads prêt à fonctionner, donc le prochain appel à yield lancera l'autre thread au lieu d'exécuter à nouveau le thread inactif.