SE2: Système d'exploitation
Printemps 2024
Instructrices: GUEALIA Nadia
Illustration courtesy of Ecy King, CS110 Champion, Spring 2021
Nous pouvons avoir une concurrence au sein d'un même processus en utilisant des threads : des séquences d'exécution indépendantes au sein d'un même processus.
Processus:
Threads:
threadUn thread peut être généré pour exécuter la fonction spécifiée avec les arguments donnés.
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
//Compile and link with -pthread.threadPour plusieurs threads, nous devons attendre un thread spécifique à la fois :
thread treads[5];
...
for (size_t i = 0; i < 5; i++) {
pthread_join(treads[i], NULL);Pour attendre la fin d'un thread, utilisez la fonction pthread_join :
static void * threadFunc(void *arg)
{// instructions
return NULL;
}
//
pthread_t mythread;
pthread_create(&mythread, NULL,threadFunc, NULL);
... // do some work
// Wait for thread to finish (blocks)
pthread_join(mythread, NULL);threadLe nouveau thread se termine de l'une des manières suivantes :
Il appelle pthread_exit(3), en spécifiant une valeur d'état de sortie qui est disponible pour un autre thread dans le même processus qui appelle pthread_join(3).
Il revient de start_routine(). Cela équivaut à appeler pthread_exit(3) avec la valeur fournie dans l'instruction return.
Il est annulé (voir pthread_cancel(3)).
L'un des threads du processus appelle exit(3), ou le thread principal effectue un retour depuis main(). Cela provoque la fin de tous les threads du processus.
#include <stdio.h>
#include <pthread.h> // for C thread support
static const int kNumFriends = 6;
static void * /* Loop 'arg' times incrementing 'glob' */
greeting(void *arg)
{
printf("Hello, world!\n");
return NULL;
}
int main(int argc, char *argv[]) {
printf("Let's hear from %d threads \n",kNumFriends);
// declare array of empty thread handles
pthread_t friends[kNumFriends];
// Spawn threads
for (size_t i = 0; i < kNumFriends; i++) {
pthread_create(&friends[i], NULL, greeting, NULL);
}
// Wait for threads
for (size_t i = 0; i < kNumFriends; i++) {
pthread_join(friends[i], NULL);
}
printf("Everyone's said hello!\n");
return 0;
}
#include <stdio.h>
#include <pthread.h> // for C thread support
static const int kNumFriends = 6;
static void * /* Loop 'arg' times incrementing 'glob' */
greeting(void *arg)
{
int * iptr =(int*)arg;
printf("Hello, world! I am thread %i \n",*iptr);
return NULL;
}
int main(int argc, char *argv[]) {
printf("Let's hear from %d threads \n",kNumFriends);
// declare array of empty thread handles
pthread_t friends[kNumFriends];
// Spawn threads
for (size_t i = 0; i < kNumFriends; i++) {
pthread_create(&friends[i], NULL, greeting, &i);
}
// Wait for threads
for (size_t i = 0; i < kNumFriends; i++) {
pthread_join(friends[i], NULL);
}
printf("Everyone's said hello!\n");
return 0;
}
thread// declare array of empty thread handles
pthread_t friends[kNumFriends];
// Spawn threads
for (size_t i = 0; i < kNumFriends; i++) {
pthread_create(&friends[i], NULL, greeting, &i);
}Nous pouvons créer un tableau de threads comme suit :
static const size_t kNumFriends = 6;
static void greeting(size_t i) {
cout << oslock << "Hello, world! I am thread " << i << endl << osunlock;
}
int main(int argc, char *argv[]) {
cout << "Let's hear from " << kNumFriends << " threads." << endl;
// declare array of empty thread handles
thread friends[kNumFriends];
// Spawn threads
for (size_t i = 0; i < kNumFriends; i++) {
friends[i] = thread(greeting, i);
}
// Wait for threads
for (size_t i = 0; i < kNumFriends; i++) {
friends[i].join();
}
cout << "Everyone's said hello!" << endl;
return 0;
}greeting(void *arg)
{
int * iptr =(int*)arg;
...
}
// Spawn threads
for (size_t i = 0; i < kNumFriends; i++) {
pthread_create(&friends[i], NULL, greeting, &i);
} for (size_t i = 0; i < kNumFriends; i++) {
pthread_create(&friends[i], NULL, greeting, &i);
}_start
greeting
main
argc
argv
i
args
args
args
args
args
args
piles de threads
main stack
Ici, nous pouvons simplement passer par copie. Mais faites attention aux conséquences de la mémoire partagée !
int main(int argc, const char *argv[]) {
thread ticketAgents[kNumTicketAgents];
size_t remainingTickets = 250;
for (size_t i = 0; i < kNumTicketAgents; i++) {
ticketAgents[i] = thread(sellTickets, i, ref(remainingTickets));
}
for (thread& ticketAgent: ticketAgents) {
ticketAgent.join();
}
cout << "Ticket selling done!" << endl;
return 0;
}static void sellTickets(size_t id, size_t& remainingTickets) {
while (remainingTickets > 0) {
sleep_for(500); // simulate "selling a ticket"
remainingTickets--;
cout << oslock << "Thread #" << id << " sold a ticket (" << remainingTickets
<< " remain)." << endl << osunlock;
}
cout << oslock << "Thread #" << id << " sees no remaining tickets to sell and exits."
<< endl << osunlock;
}thread #1
thread #2
thread #3
remainingTickets = 1
static void sellTickets(size_t id, size_t& remainingTickets) {
while (remainingTickets > 0) {
sleep_for(500); // simulate "selling a ticket"
remainingTickets--;
cout << oslock << "Thread #" << id << " sold a ticket (" << remainingTickets
<< " remain)." << endl << osunlock;
}
cout << oslock << "Thread #" << id << " sees no remaining tickets to sell and exits."
<< endl << osunlock;
}thread #1
thread #2
thread #3
Line 2: checking if there are tickets left. Yep!
remainingTickets = 1
static void sellTickets(size_t id, size_t& remainingTickets) {
while (remainingTickets > 0) {
sleep_for(500); // simulate "selling a ticket"
remainingTickets--;
cout << oslock << "Thread #" << id << " sold a ticket (" << remainingTickets
<< " remain)." << endl << osunlock;
}
cout << oslock << "Thread #" << id << " sees no remaining tickets to sell and exits."
<< endl << osunlock;
}thread #1
thread #2
thread #3
Line 2: checking if there are tickets left. Yep!
remainingTickets = 1
z
z
z
static void sellTickets(size_t id, size_t& remainingTickets) {
while (remainingTickets > 0) {
sleep_for(500); // simulate "selling a ticket"
remainingTickets--;
cout << oslock << "Thread #" << id << " sold a ticket (" << remainingTickets
<< " remain)." << endl << osunlock;
}
cout << oslock << "Thread #" << id << " sees no remaining tickets to sell and exits."
<< endl << osunlock;
}thread #1
thread #2
thread #3
Line 2: checking if there are tickets left. Yep!
remainingTickets = 1
z
z
z
z
z
z
static void sellTickets(size_t id, size_t& remainingTickets) {
while (remainingTickets > 0) {
sleep_for(500); // simulate "selling a ticket"
remainingTickets--;
cout << oslock << "Thread #" << id << " sold a ticket (" << remainingTickets
<< " remain)." << endl << osunlock;
}
cout << oslock << "Thread #" << id << " sees no remaining tickets to sell and exits."
<< endl << osunlock;
}thread #1
thread #2
thread #3
Line 4: Selling ticket!
remainingTickets = 0
z
z
z
z
z
z
static void sellTickets(size_t id, size_t& remainingTickets) {
while (remainingTickets > 0) {
sleep_for(500); // simulate "selling a ticket"
remainingTickets--;
cout << oslock << "Thread #" << id << " sold a ticket (" << remainingTickets
<< " remain)." << endl << osunlock;
}
cout << oslock << "Thread #" << id << " sees no remaining tickets to sell and exits."
<< endl << osunlock;
}thread #1
thread #2
thread #3
Line 4: Selling ticket!
remainingTickets = <really large number>
z
z
z
static void sellTickets(size_t id, size_t& remainingTickets) {
while (remainingTickets > 0) {
sleep_for(500); // simulate "selling a ticket"
remainingTickets--;
cout << oslock << "Thread #" << id << " sold a ticket (" << remainingTickets
<< " remain)." << endl << osunlock;
}
cout << oslock << "Thread #" << id << " sees no remaining tickets to sell and exits."
<< endl << osunlock;
}thread #1
thread #2
thread #3
Line 4: Selling ticket!
remainingTickets = <really large number - 1>
There is a race condition here!
static void sellTickets(size_t id, size_t& remainingTickets) {
while (remainingTickets > 0) {
sleep_for(500); // simulate "selling a ticket"
remainingTickets--;
...
}
static void sellTickets(size_t id, size_t& remainingTickets) {
while (remainingTickets-- > 0) {
sleep_for(500); // simulate "selling a ticket"
...
}// gets remainingTickets
0x0000000000401a9b <+36>: mov -0x20(%rbp),%rax
0x0000000000401a9f <+40>: mov (%rax),%eax
// Decrements by 1
0x0000000000401aa1 <+42>: lea -0x1(%rax),%edx
// Saves updated value
0x0000000000401aa4 <+45>: mov -0x20(%rbp),%rax
0x0000000000401aa8 <+49>: mov %edx,(%rax)// gets remainingImages
0x0000000000401a9b <+36>: mov -0x20(%rbp),%rax
0x0000000000401a9f <+40>: mov (%rax),%eax
// Decrements by 1
0x0000000000401aa1 <+42>: lea -0x1(%rax),%edx
// Saves updated value
0x0000000000401aa4 <+45>: mov -0x20(%rbp),%rax
0x0000000000401aa8 <+49>: mov %edx,(%rax)
Next time: introducing mutexes