Q163: Concurrent Threads Contd.

This question is in continuation of Question 44.

Answer the question below based on the following code:


Global Variable:

int count = 0;

Thread1:

for(int i = 1; i < 50000; i++)

count++;

Thread2:

for(int i = 1; i < 20000; i++)

count++;

 

Question #163: What can be said about the value of count?

Option:

  1. Value of count will always be less than or equal to 70000.
  2. Value of count will always be equal to 70000.
  3. Value of count will always be greater than or equal to 70000.
  4. Nothing can be said about value of count.

Solution: The correct answer is the first one. When there is no interleaving, value might be equal to 70000. When there is interleaving, value can only be less than 70000, and not greater.

Q160: Threads over processes?

Question160: Is there any benefit using threads over processes?

Options:

  1. No, processes are better as they provide better isolation.
  2. Yes, threads are beneficial as they are light weight to spawn.
  3. Yes, threads share code segment thereby saving memory.
  4. Both B and C depending on use case.

Solution: The correct answer is the last one. Threads are kind of light weight processes, they also share code segment with each other within the same process. Threads are useful for lightweight tasks whereas processes are useful for heavy weight tasks.

Q158: Hardware support for scheduling

Question #159: In operating systems, scheduler is a piece of code that is used to switch the CPU between the processes or threads. Is there a need of any hardware support for scheduling in Operating systems?

  1.  No, there is no need. Everything can be done purely in software.
  2.  Yes, only an interrupt controller is needed.
  3.  Yes, an interrupt controller and a hardware timer is needed.
  4.  None of the above.

 

Solution:

The correct answer is the 3rd one. An interrupt controller and a hardware timer is needed such that the scheduler can be invoked after every timer interrupt. If there is no external interrupt, there is no way by which the scheduler runs other processes or threads in the system.

Q157: About Spinlocks

Spinlock is a lock which causes a thread trying to acquire it to simply wait in a loop while repeatedly checking if the lock is available. This is similar to busy-waiting, since the thread is active but is not doing any useful work.

Question #157: Which of the following makes sense regarding spinlocks?

Options:

1. Spinlock is more useful for uni-processor systems, than multiprocessor systems.

2. Spinlock is more useful for multiprocessor systems, than uni-processor systems.

3. Spinlocks can be useful as they avoid the overhead of context switch and CPU scheduling.

4. Acquiring a lock cannot happen without spinlocks.

Solution: In a uni-processor system, if a thread has nothing better to do than just wait, it makes more sense to release the control of processor and go to sleep, so that some other thread/process can take over the processor and do some useful work. However, in a multiprocessor system, it’s okay for a thread to waste a few CPU cycles in spinlock, since other processes can still run on the other processors during that time. Imagine a scenario where process A is running and it  holds lock L, but a higher priority process B comes along, causes context switch and gets hold of the CPU. Now, process B starts running but needs lock L. If process B goes into spin lock, then all the CPU cycles are wasted as unless process A is allowed to run, it cannot release L. Hence option 2 is right over option 1.

Option 3 is also correct, since if a process has to wait for a short time, and then run again, it makes sense to use spinlocks than to do context switches. Option 4 is false, since it is not necessary to implement spinlock for acquiring locks. A thread can go to sleep if a lock is unavailable and be woken up when a lock becomes available again.

So both options  2 & 3 are correct here.

Q132: Output of the following multithreaded program?

Question #132: What will be the output of the following program?

#include <pthread.h>

#define NUM_THREADS 4

void *work(void *i){

  printf("Hello, world from %i\n", pthread_self());

  pthread_exit(NULL);

}

int main(int argc, char **argv){

  int i;

  pthread_t id[NUM_THREADS];

  for(i = 0; i < NUM_THREADS; ++i){

    if(pthread_create(&id[i], NULL, work, NULL)){

      printf("Error creating the thread\n"); exit(19);

    }

  }

  printf("After creating the thread. My id is: %i\n",

  pthread_self());

  return 0;

}

A) Hello, world from 1

Hello, world from 3

Hello, world from 4

After creating the thread. My id is: 2

 

B) Hello, world from 3

Hello, world from 2

Hello, world from 4

After creating the thread. My id is: 2

After creating the thread. My id is: 3

After creating the thread. My id is: 4

 

C) Hello, world from 4

Hello, world from 3

Hello, world from 2

After creating the thread. My id is: 1

 

D) Can’t predict

Solution: The correct answer is D. Thread inter-leavings cannot be predicted because of concurrency.

Q44: Concurrent Threads

Answer the question below based on the following code:


Global Variable:

int count = 0;

Thread1:

for(int i = 1; i &lt; 50000; i++)

count++;

Thread2:

for(int i = 1; i &lt; 20000; i++)

count++;

 

Question #44: Given that the two threads run concurrently, what will be the value of count at the end of execution of the two threads?

Options:

  • Value of count will be 70000.
  • Value of count can’t be predicted.
  • There is a race condition in the above program.
  • Both B and C.

Solution: count++ is not an atomic statement and is broken down into many instructions while executing. Following is an illustration of how things can go wrong:

Thread 1 Thread 2   Integer value
0
read value 0
read value 0
increase value 0
increase value 0
write back 1
write back 1

Hence, 4th option is the correct solution.

Q42: Threads and stack

Question #42: Do threads need different stack?

  1. Yes, they have different execution tracks, hence they need different stacks.
  2. Sometimes they need.
  3. No, they have common code segment, hence they don’t need different stacks.
  4. None of the above.

 

Solution:

Stack grows and shrinks as and when threads take different paths. And, multiple threads in same or different processes can take different execution paths, hence a dedicated stack is needed for all of the threads to execute. Therefore, the correct answer is option 1.

Q32: fork && fork

Question #32: How many total processes will be spawned after executing following program?

int main()

{

fork() && fork();

}

A)     2

B)      3

C)      4

D)     1

Solution: The system call fork() returns PID for the parent but returns 0 for the child. So after the first fork(), the child process returns 0 and will not execute the next fork since there is a logical AND operator after that. 0 AND with anything is 0, hence the next fork() won’t be executed, there by totaling 3 processes. Hence, the correct answer is B.