I currently have a program that simulates a server with multiple connections to multiple clients, with each client having its own thread to print something, and the server echoes it back.
Everything works just fine but I am using detached threads. I'd like to be able to use join-able threads to personally make sure each connection/thread is dealt with (i.e. through the use of pthread_join).
However, I'm still a beginner with threads and am unaware if there is a simple way to do this without reinventing the wheel.
I currently initialize an array of pthreads, then use pthread_create on each element incrementally until they have all been used. The main thread then waits until at least one of the connections closes then creates a new thread and gives it to the next waiting connection.
But my goal is to be able to not just let the thread be released after the connection using it has been closed, but to be able to return it to the array and remain 'open and waiting' to be handed off to the next connection. (not aware if I can do this or not).
At the end of the main thread when all connections have been closed, I would use pthread_join on each open and waiting thread in the array, successfully handling them.
Is there a way to make a thread detach from a connection but still be intact and waiting to be handed off to another connection?
In other words: is it possible for me to initialize a fixed pool of threads at the beginning of my main function to be used and released by different connections/functions then closed at the end? I'm currently creating threads as they are needed then sending them off into the abyss when the connection using them is closed. The array I am using is more of a placeholder for threads to be created, not necessarily a fixed pool of open and waiting threads.
Here is the relevant code for reference ( the most relevant parts are the 'quit' block in server_connection and the while loop in main() ). I've left out the implementation of certain irrelevant functions to save space:
void serve_connection (void* sockfd) {
ssize_t n, result;
char line[MAXLINE];
connection_t conn;
connection_init (&conn);
conn.sockfd = (int)sockfd;
while (! shutting_down) {
if ((n = readline (&conn, line, MAXLINE)) == 0) goto quit;
/* connection closed by other end */
if (shutting_down) goto quit;
if (n < 0) {
perror ("readline failed");
goto quit;
}
result = writen (&conn, line, n);
if (shutting_down) goto quit;
if (result != n) {
perror ("writen failed");
goto quit;
}
}
quit:
pthread_mutex_lock(&lock);
--running;
pthread_mutex_unlock(&lock);
close (conn.sockfd);
pthread_cond_signal(&con);
}
/* shared data between threads */
int running;
pthread_mutex_t lock;
pthread_cond_t con;
int
main (int argc, char **argv) {
/* numConn is the number of connections
running is the number of currently running threads */
running = 0;
int connfd, listenfd, numConn;
socklen_t clilen;
struct sockaddr_in cliaddr;
if(argc < 2) numConn = 4;
else {numConn = atoi(argv[1]);}
pthread_t threads[numConn];
/* make all threads detached */
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
/* initialize lock and con to protect 'running' variable */
pthread_mutex_init(&lock, NULL);
pthread_cond_init(&con, NULL);
install_siginthandler();
open_listening_socket (&listenfd);
listen (listenfd, numConn);
/* allow a default of 4 queued connection requests (unless otherwise specified) before refusing */
while (! shutting_down) {
errno = 0;
clilen = sizeof (cliaddr); /* length of address can vary, by protocol */
/* wait for running threads to close if numConn has been exceeded */
pthread_mutex_lock(&lock);
while(running == numConn)
{
pthread_cond_wait(&con, &lock);
}
pthread_mutex_unlock(&lock);
if ((connfd = accept (listenfd, (struct sockaddr *) &cliaddr, &clilen)) < 0) {
if (errno != EINTR) ERR_QUIT ("accept");
/* otherwise try again, unless we are shutting down */
} else {
pthread_create(&threads[running], &attr, (void*)&serve_connection, (void*)connfd);
pthread_mutex_lock(&lock);
++running;
pthread_mutex_unlock(&lock);
/* server_handoff (connfd); process the connection */
}
}
/* waiting for any open threads */
pthread_mutex_lock(&lock);
while(running > 0)
{
printf("Waiting on thread #%d\n", running);
pthread_cond_wait(&con, &lock);
}
pthread_mutex_unlock(&lock);
pthread_cond_destroy(&con);
pthread_mutex_destroy(&lock);
close (listenfd);
return 0;
}
Note: this is on a UNIX shell. The shutting_down variable is set to true when a signal to kill the process is sent (in this case ctrl-c).
Aucun commentaire:
Enregistrer un commentaire