Synchronizing Threads with POSIX Semaphores

Synchronizing Threads with POSIX Semaphores

  1. Why semaphores?
  2. Posix semaphores are easy to use
  3. Exercises   1   2   3   4

 


Now it is time to take a look at some code that does something a little unexpected. The program threadadd.c creates two new threads, both of which increment a global variable called count exactly NITER, with NITER = 1,000,000. But the program produces unexpected results.

 


Exercise 1. Create a directory called posixsem in your class Unix directory. Download in this directory the code threadadd.c and compile it using

     gcc threadadd.c -o threadadd -lpthread 

 

Run the executable threadadd and observe the ouput. Try it on both tanner and felix.

Quite unexpected! Since count starts at 0, and both threads increment it NITER times, we should see count equal to 2*NITER at the end of the program. Something fishy is going on here.

 


Threads can greatly simplify writing elegant and efficient programs. However, there are problems when multiple threads share a common address space, like the variable count in our earlier example.

To understand what might happen, let us analyze this simple piece of code:

      THREAD 1                THREAD 2
      a = data;               b = data;
      a++;                    b--;
      data = a;               data = b;

Now if this code is executed serially (for instance, THREAD 1 first and then THREAD 2), there are no problems. However threads execute in an arbitrary order, so consider the following situation:

 

Thread 1Thread 2 data
a = data; ---0
a = a+1; ---0
---b = data;  // 0 0
---b = b + 1; 0
data = a;  // 1 ---1
---data = b;  // 1 1

So data could end up +1, 0, -1, and there is NO WAY to know which value! It is completely non-deterministic!

The solution to this is to provide functions that will block a thread if another thread is accessing data that it is using.

Pthreads may use semaphores to achieve this.


Posix semaphores

All POSIX semaphore functions and types are prototyped or defined in semaphore.h. To define a semaphore object, use

      sem_t sem_name;


To initialize a semaphore, use sem_init():

      int sem_init(sem_t *sem, int pshared, unsigned int value);

 

  • sem points to a semaphore object to initialize
  • pshared is a flag indicating whether or not the semaphore should be shared with fork()ed processes. LinuxThreads does not currently support shared semaphores
  • value is an initial value to set the semaphore to

Example of use:

      sem_init(&sem_name, 0, 10);



To wait on a semaphore, use sem_wait:

      int sem_wait(sem_t *sem);

 

Example of use:

      sem_wait(&sem_name); 

 

  • If the value of the semaphore is negative, the calling process blocks; one of the blocked processes wakes up when another process calls sem_post.


To increment the value of a semaphore, use sem_post:

      int sem_post(sem_t *sem);

 

 

Example of use:

 

 

  • It increments the value of the semaphore and wakes up a blocked process waiting on the semaphore, if any.


To find out the value of a semaphore, use

      int sem_getvalue(sem_t *sem, int *valp);

 

  • gets the current value of sem and places it in the location pointed to by valp

Example of use:

      int value; 
      sem_getvalue(&sem_name, &value); 
      printf("The value of the semaphors is %d/n", value);


To destroy a semaphore, use

      int sem_destroy(sem_t *sem);

  • destroys the semaphore; no threads should be waiting on the semaphore if its destruction is to succeed.

Example of use:

     sem_destroy(&sem_name); 



Using semaphores - a short example

 

 

Consider the problem we had before and now let us use semaphores:

 

 
      Declare the semaphore global (outside of any funcion):

            sem_t mutex;

      Initialize the semaphore in the main function:
     
            sem_init(&mutex, 0, 1);

 

Thread 1Thread 2 data
sem_wait (&mutex); ---0
---sem_wait (&mutex); 0
a = data; /* blocked */0
a = a+1; /* blocked */0
data = a; /* blocked */1
sem_post (&mutex); /* blocked */1
/* blocked */b = data; 1
/* blocked */b = b + 1; 1
/* blocked */data = b; 2
/* blocked */sem_post (&mutex); 2
[data is fine. The data race is gone.]

 


Exercise 2.Use the example above as a guide to fix the program threadadd.c, so that the program always produces the expected output (the value 2*NITER).

To compile a program that uses pthreads and posix semaphores, use

     gcc -o filename filename.c -lpthread -lrt 



Exercise 3. Download this (incomplete) producer-consumer code in your posixsem directory (call it PC.c). The producer and the consumer share a buffer with four character slots.

Extend this code to implement a solution to the producer consumer problem using Posix threads and semaphores. Assume that there is only one producer and one consumer. The output of your code should be similar to the following:

      Producing A ...
      Producing B ...
      Producing C ...
      Producing D ...
      ------> Consuming A ...
      ------> Consuming B ...
      ------> Consuming C ...
      ------> Consuming D ...
      Producing E ...
      Producing F ...
      Producing G ...
      Producing H ...
      ------> Consuming E ...
      ------> Consuming F ...
      ------> Consuming G ...
      ------> Consuming H ...
      Producing I ...
      Producing J ...
      ------> Consuming I ...
      ------> Consuming J ...

To compile a program that uses pthreads and posix semaphores, use

     gcc -o filename filename.c -lpthread -lrt 



Exercise 4. Modify the code from exercise 3 to work with multiple producers and multiple consumers. Create three producers and three consumers in the main function and try to interleave their execution by making them sleep for a while. Comment well your code. Compile and run your program and observe the output. Label each line in the output by the identifier for each producer and consumer (P1, P2, P3, C1, C2, C3). The output of your program should be similar to the following:

      [P1] Producing A ...
      [P1] Producing B ...
       ------> [C1] Consuming A ...
       ------> [C1] Consuming B ...
      [P2] Producing A ...
      [P2] Producing B ...
       ------> [C2] Consuming A ...
       ------> [C2] Consuming B ...
      [P3] Producing A ...
      [P3] Producing B ...
       ------> [C3] Consuming A ...
       ------> [C3] Consuming B ...
      [P1] Producing C ...
      [P1] Producing D ...
       ------> [C1] Consuming C ...
       ------> [C1] Consuming D ...
      [P2] Producing C ...
      [P2] Producing D ...
       ------> [C2] Consuming C ...
       ------> [C2] Consuming D ...
      [P3] Producing C ...
      [P3] Producing D ...
       ------> [C1] Consuming C ...
      [P2] Producing E ...
       ------> [C2] Consuming D ...
      [P3] Producing E ...
       ------> [C3] Consuming E ...
       ------> [C3] Consuming E ...
      [P1] Producing E ...
       ------> [C3] Consuming E ...
      [P2] Producing F ...
       ------> [C2] Consuming F ...
      [P3] Producing F ...
       ------> [C1] Consuming F ...
      [P1] Producing F ...
       ------> [C3] Consuming F ...


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值