Threads

  1. Thread 1 : How do I multithread an application? What does that mean? What is a thread?
  2. Thread 2 : How do I design an application so that it runs lengthy operations in the background rather than making the user wait? For example, my application has to recalculate an aerodynamic model and I dont want the user interface to hang for half an hour during the computation.
  3. Thread 3 : How do I create separate threads that can handle high priority events successfully without monopolizing the system?
  4. Thread 4 : How does the scheduling and priority system work?
  5. Thread 5 : Indicate the trueness of the following statements by marking them "True" or "False". Briefly justify your answers.
    1. A program with multiple user-level threads will block completely if one of the threads makes a blocking system call.
    2. C-library calls "printf" and "scanf" are thread-safe.
    3. A system call or a library routine using global variable (e.g. errno in C programs) is not thread-safe.
  6. Thread 6 : Unix shells (e.g. ksh, csh) fork a process to execute each user command. What are the dangers in using threads inside the shell process to execute user commands?
  7. Thread 7 : Which thread synchronization mechanism (e.g. locks, semaphores, condition variables) will you use for the following situations:
    (i) Only one thread should read/write a file at a time.
    (ii) Implementing a "Pipe" mechanism using a ordinary file shared between a writer and a reader.
  8. Thread 8 (mutex) : Review the program segment given below:
    /* include stdio.h, stdlib.h, pthread.h */
    /* specify pthread libraries during compilation, cc -lpthread ... */
    int account1balance = 100 ; int account2balance = 200 ; int transferAmount
    = 20
    ;
    pthread_mutex_t pm = PTHREAD_MUTEX_INITIALIZER;
    
    void L (pthread_mutex_t *tmp ) { pthread_mutex_lock(tmp); }
    void U (pthread_mutex_t *tmp ) { pthread_mutex_unlock(tmp); }
    void *first ( ) { L(&pm); account1balance -= transferAmount; U(&pm); }
    void *second ( ) { L(&pm); account2balance += transferAmount; U(&pm); }
    
    void main( int argc, char* argv[]){
      char NL[] = "\\\n" ;
      pthread_t t1, t2;
      pthread_create( &t1, NULL, first, NULL); 
      pthread_create( &t2, NULL, second, NULL);
      L(pm); 
      printf("PreJoin:%d and %d %s", account1balance, account2balance, NL); 
      U(pm);
      pthread_join( t1, NULL); 
      pthread_join( t2, NULL);
      printf("After:%d and %d %s", account1balance, account2balance, NL) ;
    }
    

    A. List the threads of execution in this program after all threads have been created.
    B. List the possible interleavings of the the threads of executions (after all threads have been created). (Hint: each thread is atomic.)
    C. Determine the output from the "printf" statements for each interleaving.
  9. Thread 9 (mutex) : Repeat questions 8A, 8B, 8C for the following program: Also determine the output ofthe program for different values of scenario (i.e. 1, 2, 3, 4, 5, 6) by identifying possible interleavings.
    /* include stdio.h, stdlib.h, pthread.h */
    /* specify pthread libraries during compilation, cc -lpthread ... */
    pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
    pthread_mutex_t m1 = PTHREAD_MUTEX_INITIALIZER;
    pthread_mutex_t mx = PTHREAD_MUTEX_INITIALIZER;
    int x = 0 ;
    int y = 23;
    int scenario;
    
    void L (pthread_mutex_t *tmp ) { pthread_mutex_lock(tmp); }
    void U (pthread_mutex_t *tmp ) { pthread_mutex_unlock(tmp); }
    void hangaround(){ int i; for(i=0; i < 10000000; i++) ; }
    int f (int t) { hangaround(); return(t+1); }
    int g (int t) { hangaround(); return(t+1); }
    void *first (void* arg )
    {
      hangaround();
      switch (scenario) {
        case 1: { x = f(x) ; break; }
        case 2: { L(&m); x = f(x); U(&m); break; }
        case 3: { L(&m1); x = f(x); U(&m); break; }
        case 4: { U(&m1); x = f(x); L(&m1); break; }
        case 5: { L(&m); L(&m); x = f(x); U(&m); U(&m); break; }
        case 6: { L(&mx); L(&m); x = f(x); U(&mx); U(&m); break; }
    
      }
    }
    void *second ( void* arg) {
      switch (scenario) {
        case 1: { x = g(y); break; }
        case 2: { L(&m); x = g(y); U(&m); break; }
        case 3: { L(&m); x = g(y); U(&m1); break; }
        case 4: { U(&m1); x = g(y); L(&m1); break; }
        case 5: { L(&m); x = g(y);  U(&m); break; }
        case 6: { L(&m); L(&mx); x = g(y); U(&m); U(&mx); break; }
      } 
    } 
    
    void main( int argc, char* argv[] ){
            pthread_t t1, t2;
            if (argc != 2) {
              printf("Usage: %s scenarioNumber(1-6) n", argv[0]);
            } else { scenario = atoi(argv[1]); }
            pthread_create( &t2, NULL, second, NULL);
            pthread_create( &t1, NULL, first, NULL);
            pthread_join( t1, NULL);
            pthread_join( t2, NULL);
            printf ("scenario %d : x = %d \n", scenario, x);
    }
    
    
    
  10. Thread 10 (semaphores) : Repeat questions 8A, 8B, 8C for the following program. Also determine the output ofthe program for different values of scenario (i.e. 1, 2, 3, 4, 5, 6) by identifying possible interleavings.
    /* include stdio.h, pthread.h, semaphore.h */
    /* specify pthread and semaphore libraries during compilation */
    /* POSIX Semaphore implementation may not be available */
    
    sem_t m, m1, mx;
    
    int x = 0 ;
    int y = 23;
    int scenario;
    
    void P (sem_t *s ) { sem_wait(s); }
    void V (sem_t *s ) { sem_post(s); }
    void *first ( void* arg)
    {
      switch (scenario) {
        case 1: { x = x + 1; break; }
        case 2: { P(&m); x = x + 1; V(&m); break; }
        case 3: { P(&m1); x = x + 1; V(&m); break; }
        case 4: { V(&m1); x = x + 1; P(&m1); break; }
        case 5: { P(&mx); P(&m); x = x + 1; V(&mx); V(&m); break; }
    
      }
    }
    void *second (void* arg ) {
      switch (scenario) {
        case 1: { x = y + 1; break; }
        case 2: { P(&m); x = y + 1; V(&m); break; }
        case 3: { P(&m); x = y + 1; V(&m1); break; }
        case 4: { V(&m1); x = y + 1; P(&m1); break; }
        case 5: { P(&m); P(&mx); x = y + 1; V(&m); V(&mx); break; }
      }
    }
    void main( char* argv[], int argc){
            pthread_t t1, t2;
            if (argc != 2) {
              printf("Usage: %s scenarioNumber(1-5)\n", argv[0]);
            } else { scenario = atoi(argv[1]) ; }
            sem_init(&m, 0, 1); /* initialize to 1 */
            sem_init(&m1, 0, 0); /* initialize to 0 */
            sem_init(&mx, 0, 1); /* initialize to 1 */
            pthread_create( &t1, NULL, first, NULL);
            pthread_create( &t2, NULL, second, NULL);
            pthread_join( t1, NULL);
            pthread_join( t2, NULL);
            printf ("scenario %d : x = %d \n", scenario, x);
    }
    
    
    
    
  11. Thread 11 : Which thread synchronization mechanism (e.g. locks, semaphores, condition variables, pthread_join) will you use for the following situations in context of producer consumer problem. Briefly justify your answer.
    (i) A thread should wait before accessing shared buffer if other thread is modifying (adding or removing an item) the buffer.
    (ii) Producer should wait if buffer is full.
    (iii) Consumer should wait if buffer is empty.
    (iv) Consumer should thread_exit if buffer is empty and producer has exited.
    (v) main thread should wait the producer and consumer threads to finish before terminating the process.

    Assume the program has three threads, namely a main thread, a producer thread, and a consumer thread. The main thread is responsible for creating the other threads and properly terminating the program.