This is also an example program for:
- How to hide input for entering password (Console ECHO OFF/ON),
- How to get input from keyboard without pressing ENTER,
- How to restrict keyboard to get only one character as input at a time,
- How to suspend/resume multiple threads effectively, etc..
After an exhaustive googling to find a perfect (clearly understandable) way of doing suspend/resume threads and having went through many not much satisfying sort of producer/consumer examples, I wrote this program.
There are many ways of doing thread suspend/resume:
- Using Mutexes and Boolean Variables,
- Using Thread Conditions,
- Using Semaphores,
- Using Signals,
- Using Message Passing Interface, etc.
This program employs a combination of Boolean Variables, Mutexes and Thread Conditions.
The program is simple. The main thread (function main()) creates/initializes and launches four threads which run simultaneously. They are:
- watch_for_user_keypress_thread, which goes into a loop and wait for user input and suspend/resume other three threads according to user keypress. This thread also handles terminal settings (i.e. tty console settings).
- func_one_thread, which is a resource independent thread that counts variable count_one and prints it in STDERR stream.
- func_two_thread, which is also resource independent and does the same process like func_one, but on counter count_two.
- func_three_thread, adds count_one and count_two and prints the result along with its own counter count in STDERR stream.
Inputs to the program are:
- 1 – is a toggle switch to suspend/resume thread_one
- 2 – is a toggle switch to suspend/resume thread_two
- 3 – is a toggle switch for thread_three
- 0 – ends watch_for_user_keypress and signals all the threads to stop their execution and quit
- Other keypress – nothing happens
The program is not complicated and is self-explanatory.
// multitask_multithread.c #include <pthread.h> #include <stdio.h> #include <termios.h> #include <time.h> #define TRUE 1 #define FALSE 0 // Current tty console settings variable static struct termios tty_state_current; // Threads 1, 2 and 3 declared as global pthread_t func_one_thread, func_two_thread, func_three_thread; // Thread conditions pthread_cond_t thread_cond_one, thread_cond_two, thread_cond_three; // Thread mutex to go with thread conditions pthread_mutex_t mutex_flag; // Boolean flags short tflag_one_on, tflag_two_on, tflag_three_on, quit_flag = FALSE; // Counters for threads 1 and 2 int count_one, count_two = 0; // tty console ECHO ON int echo_on() { struct termios tty_state; if(tcgetattr(0, &tty_state) < 0) return -1; tty_state.c_lflag |= ECHO; return tcsetattr(0, TCSANOW, &tty_state); } // tty console ECHO OFF int echo_off() { struct termios tty_state; if(tcgetattr(0, &tty_state) < 0) return -1; tty_state.c_lflag &= ~ECHO; return tcsetattr(0, TCSANOW, &tty_state); } // Restore tty console settings to its previous original state void restore_terminal_settings(void) { tcsetattr(0, TCSANOW, &tty_state_current); } // Disable waiting for ENTER and accept only one keypress as input void disable_waiting_for_enter(void) { struct termios tty_state; tcgetattr(0, &tty_state_current); // Get current tty console settings tty_state = tty_state_current; tty_state.c_lflag &= ~(ICANON | ECHO); tcsetattr(0, TCSANOW, &tty_state); atexit(restore_terminal_settings); // Very handy function to restore settings } // Take user keypress and suspend/resume other threads accordingly void *watch_for_user_keypress() { char getKeyPress; disable_waiting_for_enter(); echo_off(); do { getKeyPress = getchar(); switch (getKeyPress) { case 49: if(!tflag_one_on) { tflag_one_on = TRUE; pthread_mutex_lock(&mutex_flag); pthread_cond_signal(&thread_cond_one); pthread_mutex_unlock(&mutex_flag); } else { tflag_one_on = FALSE; } break; case 50: if(!tflag_two_on) { tflag_two_on = TRUE; pthread_mutex_lock(&mutex_flag); pthread_cond_signal(&thread_cond_two); pthread_mutex_unlock(&mutex_flag); } else { tflag_two_on = FALSE; } break; case 51: if(!tflag_three_on) { tflag_three_on = TRUE; pthread_mutex_lock(&mutex_flag); pthread_cond_signal(&thread_cond_three); pthread_mutex_unlock(&mutex_flag); } else { tflag_three_on = FALSE; } } } while(getKeyPress != 48); quit_flag = TRUE; pthread_cond_broadcast(&thread_cond_one); pthread_cond_broadcast(&thread_cond_two); pthread_cond_broadcast(&thread_cond_three); //echo_on(); // No need for this since atexit() restores previous settings pthread_exit(NULL); } void *func_one() { pthread_mutex_lock(&mutex_flag); pthread_cond_wait(&thread_cond_one, &mutex_flag); pthread_mutex_unlock(&mutex_flag); if(quit_flag) pthread_exit(NULL); while(TRUE) { if(!tflag_one_on) { pthread_mutex_lock(&mutex_flag); pthread_cond_wait(&thread_cond_one, &mutex_flag); pthread_mutex_unlock(&mutex_flag); } if(quit_flag) break; fprintf(stderr, "<<--%d-->> ", ++count_one); sleep(1); } pthread_exit(NULL); } void *func_two() { pthread_mutex_lock(&mutex_flag); pthread_cond_wait(&thread_cond_two, &mutex_flag); pthread_mutex_unlock(&mutex_flag); if(quit_flag) pthread_exit(NULL); while(TRUE) { if(!tflag_two_on) { pthread_mutex_lock(&mutex_flag); pthread_cond_wait(&thread_cond_two, &mutex_flag); pthread_mutex_unlock(&mutex_flag); } if(quit_flag) break; fprintf(stderr, "<<oo%doo>> ", ++count_two); sleep(1); } pthread_exit(NULL); } void *func_three() { int count = 0; pthread_mutex_lock(&mutex_flag); pthread_cond_wait(&thread_cond_three, &mutex_flag); pthread_mutex_unlock(&mutex_flag); if(quit_flag) pthread_exit(NULL); while(TRUE) { if(!tflag_three_on) { pthread_mutex_lock(&mutex_flag); pthread_cond_wait(&thread_cond_three, &mutex_flag); pthread_mutex_unlock(&mutex_flag); } if(quit_flag) break; fprintf(stderr, "<<==%d::%d==>> ", ++count, count_one+count_two); sleep(1); } pthread_exit(NULL); } int main(int argc, char *argv[]) { pthread_t watch_for_user_keypress_thread; // Thread mutex initialization pthread_mutex_init(&mutex_flag, NULL); // Thread condtions initialization pthread_cond_init(&thread_cond_one, NULL); pthread_cond_init(&thread_cond_two, NULL); pthread_cond_init(&thread_cond_three, NULL); // Thread creation pthread_create(&watch_for_user_keypress_thread, NULL, watch_for_user_keypress, NULL); pthread_create(&func_one_thread, NULL, func_one, NULL); pthread_create(&func_two_thread, NULL, func_two, NULL); pthread_create(&func_three_thread, NULL, func_three, NULL); // main() launches these four threads and starts waiting for their completion pthread_join(watch_for_user_keypress_thread, NULL); pthread_join(func_one_thread, NULL); pthread_join(func_two_thread, NULL); pthread_join(func_three_thread, NULL); // All threads exited normally printf("\n"); return 0; }
Compile it.
$ gcc multitask_multithread.c -o multitask_multithread -lpthread
The output looks like:
$ ./multitask_multithread
<<–1–>> <<–2–>> <<–3–>> <<–4–>> <<–5–>> <<–6–>> <<oo1oo>> <<–7–>> <<oo2oo>> <<–8–>> <<oo3oo>> <<–9–>> <<oo4oo>> <<–10–>> <<oo5oo>> <<–11–>> <<oo6oo>> <<–12–>> <<–13–>> <<–14–>> <<–15–>> <<–16–>> <<–17–>> <<–18–>> <<==1::24==>> <<==2::24==>> <<==3::24==>> <<oo7oo>> <<==4::25==>> <<oo8oo>> <<==5::26==>> <<oo9oo>> <<==6::27==>> <<–19–>> <<oo10oo>> <<==7::29==>> <<–20–>> <<oo11oo>> <<==8::31==>> <<–21–>> <<oo12oo>> <<–22–>> <<oo13oo>> <<–23–>> <<oo14oo>> <<–24–>> <<–25–>> <<–26–>> <<–27–>> <<–28–>> <<–29–>> <<–30–>> <<oo15oo>> <<–31–>> <<oo16oo>> <<–32–>> <<oo17oo>> <<–33–>> <<==9::50==>> <<oo18oo>> <<–34–>> <<==10::52==>> <<oo19oo>> <<–35–>> <<==11::54==>> <<oo20oo>> <<–36–>> <<==12::56==>> <<oo21oo>> <<–37–>> <<==13::58==>> <<oo22oo>>
Note 1: Bad programming ethics. Any outputs of a program are not supposed to be printed onto error (STDERR) stream. Only errors should be printed using STDERR. This sort of bad ethics affects the usage of standard pipes “>”, “<” and “|” and other operations.
Note 2: Instead of printing normal output onto STDERR, we can use functions like kbhit()/getch() or other workarounds that suppress “keyboard input wait” hindering background threads printing onto STDOUT stream.
Thanks for the read and please leave comments 🙂
Thank you for a great post.
toggle switches are very handy specially if your project requires momentary on switch.*
<a href="Our new internet page
http://www.caramoantravel.com/caramoan-accommodation/
Thank you very much.
You’re welcome.
Brian Bernhard – thank you very very much for posting this. I am new to multi thread programming and having a working example is extremly needed. THANK YOU.
You’re welcome. Glad it helped you.
thank you. this is very simple!
You’re welcome.
You are a live saviour
You’re welcome.
thank you so much. Good program
You’re welcome. Glad it helped you.