Chinaunix首页 | 论坛 | 博客
  • 博客访问: 23259
  • 博文数量: 11
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 18
  • 用 户 组: 普通用户
  • 注册时间: 2013-05-31 22:46
文章分类
文章存档

2013年(11)

我的朋友

分类: LINUX

2013-06-07 13:27:53

一、线程同步
线程同步的两种基本方法:
A. 信号量
   它的作用如同看守一段代码的看门人。
   控制一组相同对象的访问时,
   比如从5条可用的电话线中分配一条给某个线程的情况,
   就更适合计算信号量。
   
   最简单的信号量--二进制信号量,它只有0和1两种取值;
   更通用的信号量--计数信号量,它有更大的取值范围。

B. 互斥量
   它的作用如同保护代码段的一个互斥设备。
   如果想控制任一时刻只能有一个线程可以访问一些共享内存,
   通常使用互斥量

二、用信号量进行同步
有两组函数用于信号量,
一组取自POSIX的实时扩展,用于线程同步。
一组被称为系统V信号量,  用于进程同步

信号量函数的名字都以sem_开头。
线程中使用的基本信号量函数有四个,它们的定义如下:


  1. #include <semaphore.h>

  2. int sem_init(sem_t *sem, int pshared, unsigned int value);
信号量通过sem_init函数创建,
参数:
sem    : 将被函数初始化信号量对象指针 
pshared: 共享选项
         0,   表示这个信号量是当前进程的局部信号量,
         非0, 表示这个信号量可以在多个进程之间共享,
              目前linux还不支持,将会导致调用失败。
value  : 信号量初始的整数值


控制信号量值的函数:
  1. #include <semaphore.h>

  2. int sem_wait(sem_t * sem);
  3. int sem_post(sem_t * sem);
sem_post: 以原子操作的方式给信号量的值加1 (V操作).
sem_wait: 以原子操作的方式给信号量的值减1 (P操作).
          但它会等待直到信号量有个非零值才会开始减法操作。
  因此,
  如果对值为2的信号量调用sem_wait,
      线程将继续执行,但信号量的值会减1.
  如果对值为0的信号量调用sem_wait,
      这个函数将会等待,
      直到有其他线程增加了该信号量值使其不再为0为止。
  如果两个线程同时在sem_wait调用上等待同一个信号量变为非零值,
      那么当该信号量被第三个线程增加1时,
      只有其中一个等待线程将开始对信号量减1,然后继续执行,
      另外一个线程还将继续等待。


  1. #include <semaphore.h>

  2. int sem_destroy(sem_t * sem);
对用完的信号量进行清理,
并清理该信号量拥有的所有资源。
如果企图清理的信号量正在被一些线程等待,将返回错误。

这些函数在成功进返回0.

示例程序:
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <stdlib.h>
  4. #include <string.h>

  5. #include <pthread.h>
  6. #include <semaphore.h>

  7. void *thread_function(void *arg);
  8. sem_t bin_sem;

  9. #define WORK_SIZE 1024
  10. char work_area[WORK_SIZE];

  11. int main()
  12. {
  13.   int res;
  14.   pthread_t a_thread;
  15.   void *thread_result;

  16.   /* 创建信号量 */
  17.   res = sem_init(&bin_sem, 0, 0);
  18.   if (res != 0)
  19.   {
  20.     perror("Semaphore initialization failed");
  21.     exit(EXIT_FAILURE);
  22.   }
  23.  
  24.   res = pthread_create(&a_thread, NULL, thread_function, NULL);
  25.   if (res != 0)
  26.   {
  27.     perror("Thread creation failed");
  28.     exit(EXIT_FAILURE);
  29.   }
  30.   
  31.   printf("Input some text. Enter 'end' to finish\n");
  32.   while(strncmp("end", work_area, 3) != 0)
  33.   {
  34.     fgets(work_area, WORK_SIZE, stdin);
  35.     sem_post(&bin_sem); //  加1
  36.   }
  37.   
  38.   printf("\nWaiting for thread to finish...\n");
  39.   res = pthread_join(a_thread, &thread_result);
  40.   if (res != 0)
  41.   {
  42.     perror("Thread join failed");
  43.     exit(EXIT_FAILURE);
  44.   }

  45.   printf("Thread joined\n");
  46.   sem_destroy(&bin_sem);
  47.   exit(EXIT_SUCCESS);
  48. }


  49. void *thread_function(void *arg)
  50. {
  51.   sem_wait(&bin_sem); //  减1
  52.   while(strncmp("end", work_area, 3) != 0)
  53.   {
  54.     printf("You input %d characters\n", strlen(work_area) -1);
  55.     sem_wait(&bin_sem);
  56.   }
  57.   pthread_exit(NULL);
  58. }

编译与运行:
  1. $ cc -D_REENTRANT thread3.c –o thread3 -lpthread
  2. $ ./thread3
  3. Input some text. Enter ‘end’ to finish
  4. The Wasp Factory
  5. You input 16 characters
  6. Iain Banks
  7. You input 10 characters
  8. end
  9. Waiting for thread to finish...
  10. Thread joined


三、用互斥量进行同步
它允许程序员锁住某个对象,使得每次只能有一个线程访问它。
必须在进入一段代码之前锁住一个互斥量,然后在完成操作之后解锁它。

操作函数如下:
  1. #include <pthread.h>

  2. int pthread_mutex_init(pthread_mutex_t *mutex,
  3.                        const pthread_mutexattr_t *mutexattr);
  4. int pthread_mutex_lock(pthread_mutex_t *mutex));
  5. int pthread_mutex_unlock(pthread_mutex_t *mutex);
  6. int pthread_mutex_destroy(pthread_mutex_t *mutex);
参数:
mutex : 声明过的对象指针
mutexattr: 互斥量的属性,默认为fast.
但它有一个缺点:
如果程序试图对一个已经加了锁的互斥量调用pthread_mutex_lock,
    程序将会被阻塞。
    而又因为拥有互斥量的这个线程正是现在被阻塞的线程,
    所以互斥量就永远也不会解锁了,
    程序就进入死锁状态。
这个问题可以通过改变互斥量的属性来解决:
    可以让它检查这种情况并返回一个错误,
    或者让它递归地操作,给同一个线程加上多个锁,
    但必须注意在后面执行同等数量的解锁操作。

返回值:
成功时,返回0;
失败时,将返回错误代码,但并不设置errno,
        所以必须对函数的返回代码进行检查。


示例代码:
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <pthread.h>
  6. #include <semaphore.h>

  7. void *thread_function(void *arg);
  8. pthread_mutex_t work_mutex; /* protects both work_area and time_to_exit */

  9. #define WORK_SIZE 1024
  10. char work_area[WORK_SIZE];
  11. int time_to_exit = 0;

  12. int main()
  13. {
  14.   int res;
  15.   pthread_t a_thread;
  16.   void *thread_result;

  17.   res = pthread_mutex_init(&work_mutex, NULL);
  18.   if (res != 0)
  19.   {
  20.     perror("Mutex initialization failed");
  21.     exit(EXIT_FAILURE);
  22.   }
  23.  
  24.   res = pthread_create(&a_thread, NULL, thread_function, NULL);
  25.   if (res != 0)
  26.   {
  27.     perror("Thread creation failed");
  28.     exit(EXIT_FAILURE);
  29.   }

  30.   pthread_mutex_lock(&work_mutex);
  31.   printf("Input some text. Enter 'end' to finish\n");
  32.   while(!time_to_exit)
  33.   {
  34.     fgets(work_area, WORK_SIZE, stdin);
  35.     pthread_mutex_unlock(&work_mutex);
  36.     while(1)
  37.     {
  38.       pthread_mutex_lock(&work_mutex);
  39.       if (work_area[0] != '\0')
  40.       {
  41.         pthread_mutex_unlock(&work_mutex);
  42.         sleep(1);
  43.       }
  44.       else
  45.       {
  46.         break;
  47.       }

  48.     }
  49.   }
  50.   
  51.   pthread_mutex_unlock(&work_mutex);
  52.   printf("\nWaiting for thread to finish...\n");
  53.   res = pthread_join(a_thread, &thread_result);
  54.   if (res != 0)
  55.   {
  56.     perror("Thread join failed");
  57.     exit(EXIT_FAILURE);
  58.   }

  59.   printf("Thread joined\n");
  60.   pthread_mutex_destroy(&work_mutex);
  61.   exit(EXIT_SUCCESS);
  62. }

  63. void *thread_function(void *arg)
  64. {
  65.   sleep(1);
  66.   pthread_mutex_lock(&work_mutex);
  67.   while(strncmp("end", work_area, 3) != 0)
  68.   {
  69.     printf("You input %d characters\n", strlen(work_area) -1);
  70.     work_area[0] = '\0';
  71.     pthread_mutex_unlock(&work_mutex);
  72.     sleep(1);
  73.     pthread_mutex_lock(&work_mutex);
  74.     while (work_area[0] == '\0' )
  75.     {
  76.       pthread_mutex_unlock(&work_mutex);
  77.       sleep(1);
  78.       pthread_mutex_lock(&work_mutex);
  79.     }
  80.   }
  81.   time_to_exit = 1;
  82.   work_area[0] = '\0';
  83.   pthread_mutex_unlock(&work_mutex);
  84.   pthread_exit(0);
  85. }


编译与运行:
  1. $ cc -D_REENTRANT thread4.c –o thread4 -lpthread
  2. $ ./thread4
  3. Input some text. Enter 'end' to finish
  4. Whit
  5. You input 4 characters
  6. The Crow Road
  7. You input 13 characters
  8. end
  9. Waiting for thread to finish...
  10. Thread joined

分析:
这里通过轮询来获得结果的方法并不是好的编程方式。
在实际编程中,
应该尽可能用信号量来避免出现这种情况。

四、线程的属性
  1. #include <pthread.h>

  2. int pthread_attr_init(pthread_attr_t *attr);
初始化一个线程对象的属性
成功时,返回0,
失败时,返回错误代码。


  1. #include <pthread.h>

  2. int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
  3. int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
  4. int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
  5. int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy);
  6. int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param
  7. *param);
  8. int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param
  9. *param);
  10. int pthread_attr_setinheritsched(pthread_attr_t *attr, int inherit);
  11. int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inherit);
  12. int pthread_attr_setscope(pthread_attr_t *attr, int scope);
  13. int pthread_attr_getscope(const pthread_attr_t *attr, int *scope);
  14. int pthread_attr_setstacksize(pthread_attr_t *attr, int scope);
  15. int pthread_attr_getstacksize(const pthread_attr_t *attr, int *scope);
detachedstate: 这个属性允许我们无需对线程进行重新合并。
   pthread_attr_setdetachstate函数可用到的两个标志分别是:
     PTHREAD_CREATE_JOINABLE, 默认值,允许两个线程重新合并。
     PTHREAD_CREATE_DETACHED, 不能调用pthread_join来获得另一个线程的退出状态。
schedpolicy:  控制线程的调度方式。
   取值可以是:
     SCHED_OTHER, 默认值, 
     SCHED_RP,    超级用户权限运行的进程,使用循环调度机制;
     SCHED_FIFO,  超级用户权限运行的进程,使用先进先出策略。
schedparam:  和schedpolicy结合使用
inheritsched: 这个属性可取两个值
     PTHREAD_EXPLICIT_SCHED, 
     PTHREAD_INHERIT_SCHED
scope: 控制一个线程调度的计算方式。
stacksize: 控制线程创建的栈大小,单位为字节

示例程序:
创建一个线程属性,
并其设置为脱离状态,
然后用这个属性创建一个线程。
子线程结束时,它照常调用pthread_exit,
但原先的线程不再等待与它创建的子线程重新合并。
主线程通过一个简单的thread_finished标志来检测子线程是否已经结束,
并显示线程之间仍然共享着变量。

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <stdlib.h>
  4. #include <pthread.h>

  5. void *thread_function(void *arg);

  6. char message[] = "Hello World";
  7. int thread_finished = 0;

  8. int main()
  9. {
  10.   int res;
  11.   pthread_t a_thread;
  12.   pthread_attr_t thread_attr;
  13.   
  14.   res = pthread_attr_init(&thread_attr);
  15.   if (res != 0)
  16.   {
  17.     perror("Attribute creation failed");
  18.     exit(EXIT_FAILURE);
  19.   }

  20.   res = pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
  21.   if (res != 0)
  22.   {
  23.     perror("Setting detached attribute failed");
  24.     exit(EXIT_FAILURE);
  25.   }

  26.   res = pthread_create(&a_thread, &thread_attr,
  27.                        thread_function, (void *)message);
  28.   if (res != 0)
  29.   {
  30.     perror("Thread creation failed");
  31.     exit(EXIT_FAILURE);
  32.   }

  33.   (void)pthread_attr_destroy(&thread_attr);
  34.   while(!thread_finished)
  35.   {
  36.     printf("Waiting for thread to say it's finished...\n");
  37.     sleep(1);
  38.   }

  39.   printf("Other thread finished, bye!\n");
  40.   exit(EXIT_SUCCESS);
  41. }

  42. void *thread_function(void *arg)
  43. {
  44.   printf("thread_function is running. Argument was %s\n", (char *)arg);
  45.   sleep(4);
  46.   printf("Second thread setting finished flag, and exiting now\n");
  47.   thread_finished = 1;
  48.   pthread_exit(NULL);
  49. }



五、取消一个线程
  1. #include <pthread.h>

  2. int pthread_cancel(pthread_t thread);
提供一个线程标识,发送请求来取消它。

线程可以用pthread_setcancelstate来设置自己的取消状态。
  1. #include <pthread.h>

  2. int pthread_setcancelstate(int state, int *oldstate);
参数:
state: 取值可以是
  PTHREAD_CANCEL_ENABLE, 允许线程接收取消请求;
  PTHREAD_CANCEL_DISABLE, 忽略取消请求。
oldstate: 用于获取先前的取消状态,
  如果没有兴趣,可以传NULL。

如果取消请求接受了,线程可以进入第二个控制层次,
用pthread_setcanceltype来设置取消类型:
  1. #include <pthread.h>

  2. int pthread_setcanceltype(int type, int *oldtype);
参数:
type : 取值为
  PTHREAD_CANCEL_ASYNCHRONOUS, 在接收到取消请求后立即执行
  PTHREAD_CANCEL_DEFERRED, 它将使得在接收到请求后,
     一直等待直到线程执行了下述函数之一后才采取行动。
     具体函数是:
       pthread_join, 
       pthread_cond_wait,
       pthread_cond_timedwait, 
       pthread_testcancel, 
       sem_wait,
       sigwait.
oldtype:保存先前的状态,可以传NULL

默认情况下,
线程在启动时
取消状态为: PTHREAD_CANCEL_ENABLE 
取消类型为: PTHREAD_CANCEL_DEFERRED.

示例程序:
主线程向它创建的线程发送一个取消请求

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <stdlib.h>
  4. #include <pthread.h>

  5. void *thread_function(void *arg);

  6. int main()
  7. {
  8.   int res;
  9.   pthread_t a_thread;
  10.   void *thread_result;
  11.   
  12.   res = pthread_create(&a_thread, NULL, thread_function, NULL);
  13.   if (res != 0)
  14.   {
  15.     perror(“Thread creation failed”);
  16.     exit(EXIT_FAILURE);
  17.   }

  18.   sleep(3);
  19.   printf(“Canceling thread...\n”);
  20.   res = pthread_cancel(a_thread);
  21.   if (res != 0)
  22.   {
  23.     perror(“Thread cancelation failed”);
  24.     exit(EXIT_FAILURE);
  25.   }

  26.   printf(“Waiting for thread to finish...\n”);
  27.   res = pthread_join(a_thread, &thread_result);
  28.   if (res != 0)
  29.   {
  30.     perror(“Thread join failed”);
  31.     exit(EXIT_FAILURE);
  32.   }

  33.   exit(EXIT_SUCCESS);
  34. }


  35. void *thread_function(void *arg)
  36. {
  37.   int i, res;
  38.   
  39.   res = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
  40.   if (res != 0)
  41.   {
  42.     perror(“Thread pthread_setcancelstate failed”);
  43.     exit(EXIT_FAILURE);
  44.   }

  45.   res = pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
  46.   if (res != 0)
  47.   {
  48.     perror(“Thread pthread_setcanceltype failed”);
  49.     exit(EXIT_FAILURE);
  50.   }

  51.   printf(“thread_function is running\n”);
  52.   for(i = 0; i < 10; i++)
  53.   {
  54.     printf(“Thread is still running (%d)...\n”, i);
  55.     sleep(1);
  56.   }
  57.   pthread_exit(0);
  58. }


编译运行:
  1. $ ./thread7
  2. thread_function is running
  3. Thread is still running (0)...
  4. Thread is still running (1)...
  5. Thread is still running (2)...
  6. Canceling thread...
  7. Waiting for thread to finish...
  8. $
六、多线程
在同一程序中创建多个线程,
然后以不同于其启动的顺序将它们合并到一起。
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <stdlib.h>
  4. #include <pthread.h>

  5. #define NUM_THREADS 6

  6. void *thread_function(void *arg);

  7. int main()
  8. {
  9.   int res;
  10.   pthread_t a_thread[NUM_THREADS];
  11.   void *thread_result;
  12.   int lots_of_threads;

  13.   for(lots_of_threads = 0; lots_of_threads < NUM_THREADS; lots_of_threads++)
  14.   {
  15.     res = pthread_create(&(a_thread[lots_of_threads]),
  16.                          NULL, thread_function, (void *)&lots_of_threads);
  17.     if (res != 0)
  18.     {
  19.       perror(“Thread creation failed”);
  20.       exit(EXIT_FAILURE);
  21.     }
  22.     sleep(1);
  23.   }

  24.   printf(“Waiting for threads to finish...\n”);
  25.   for(lots_of_threads = NUM_THREADS - 1; lots_of_threads >= 0lots_of_threads--)
  26.   {
  27.     res = pthread_join(a_thread[lots_of_threads], &thread_result);
  28.     if (res == 0)
  29.     {
  30.       printf(“Picked up a thread\n”);
  31.     }
  32.     else
  33.     {
  34.       perror(“pthread_join failed”);
  35.     }
  36.   }

  37.   printf(“All done\n”);
  38.   exit(EXIT_SUCCESS);
  39. }

  40. void *thread_function(void *arg)
  41. {
  42.   int my_number = *(int *)arg;
  43.   int rand_num;

  44.   printf(“thread_function is running. Argument was %d\n”, my_number);

  45.   rand_num=1+(int)(9.0*rand()/(RAND_MAX+1.0));
  46.   sleep(rand_num);

  47.   printf(“Bye from %d\n”, my_number);
  48.   pthread_exit(NULL);
  49. }

编译与运行:
  1. $ ./thread8
  2. thread_function is running. Argument was 0
  3. thread_function is running. Argument was 1
  4. thread_function is running. Argument was 2
  5. thread_function is running. Argument was 3
  6. thread_function is running. Argument was 4
  7. Bye from 1
  8. thread_function is running. Argument was 5
  9. Waiting for threads to finish...
  10. Bye from 5
  11. Picked up a thread
  12. Bye from 0
  13. Bye from 2
  14. Bye from 3
  15. Bye from 4
  16. Picked up a thread
  17. Picked up a thread
  18. Picked up a thread
  19. Picked up a thread
  20. Picked up a thread
  21. All done

阅读(742) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~