UC编程之线程

线程--隶属于进程,是进程中的程序流。操作系统支持多进程,每个进程内部支持多线程。多线程并行(同时执行)代码。

 进程--重量级的,每个进程都需要独立的内存空间。

 线程--轻量级的,线程不拥有独立的内存资源,共享所在进程的内存资源,

      但每个线程都拥有一个独立的栈区。

 开发程序中,多线程使用的概率更高。

 进程中可以有多个线程,但必须有一个主线程(main函数)

 

 由于CPU不可分,真正意义的并行其实不存在的,针对时间点的并行是不存在 

 的。人的感官都是针对时间段(很少)。主流的操作系统都是使用CPU时间片的 

 方式实现并行。每个CPU时间片都是极少的CPU运行时间。

 

 多线程编程有一套成形的API,属于POSIX规范。

 POSIX规范提供了一个头文件pthread.h 和一个共享库文件lib pthread.so

 因此在编写多线程代码时需要#include<pthread.h>,链接时需要加 -l pthread 

 或 -pthread

 

如何创建线程?

 关于线程的大多数函数/成员变量都是以pthread_开头。

 int pthread_create(pthread_t *id, const pthread_attr_t *attr,

                 void *(fa) (void *), void *p)

 参数id用于存储新建线程的ID

 attr用于传入新建线程的属性,一般0即可

 fa是函数指针,线程执行的代码写成函数fa并在创建线程时传入

 pfa的参数,如果不需要0即可

 注意:线程的错误处理不使用errno,而是直接返回。

 当同一进程内部有多线程时,每个线程内部的代码顺序执行,而多线程之间的

 代码乱序执行。

 多线程之间 相互独立,但也相互影响。

 主线程结束,进程随之结束;进程结束,进程中所有线程都结束。

 pthread_join函数挂起当前线程直到所等待的线程结束。

 线程的参数由 pthread_create的第4个参数传入,返回值由pthread_join的第二

 个参数获取。

 函数pthread_exit(void*)也可以退出线程并且也有返回值(参数void*就是线程的

 返回值)

 函数exit()退出的是进程(所有线程)pthread_exit()退出单个线程。

 

 线程的运行有两种状态:

   非分离状态--可以用函数pthread_join

   分离状态 --可以用函数pthread_detach

 两者的区别:

   非分离状态的线程资源回收要到pthread_join函数结束以后;分离状态的线程资源马上回收。处于分离状态的线程无法join(join没效果)

经验:线程启动后,最好设置join或者detach

实例:

  创建线程:

      1 #include<stdio.h>

      2 #include<pthread.h>

      3 

      4 void* task(void* p){

      5   int i;

      6   for(i=0;i<100;i++){

      7     printf("i=%d\n",i);

      8   }

      9 }

     10 

     11 

     12 int main(){//主线程

     13     pthread_t id;

     14     pthread_create(&id,0,task,0);//创建线程

     15     int i;

     16     for(i=0;i<100;i++){

     17       printf("main=%d\n",i);

     18     }

     19      sleep(1);

     20      pthread_t id2 = pthread_self();//取当前线程

     21      printf("id=%u,id2=%u\n",id);

     22     return 0;

     23 }

 

等待线程(pthread_join):

      1 #include<stdio.h>

      2 #include<pthread.h>

      3 

      4 void* task(void* p){

      5   int* pi = p;

      6   printf("*pi=%d\n",*pi);

      7   *pi =200;

      8 }

      9 int main(){//主线程

     10     pthread_t id1; 

     11     int x =100;

     12     pthread_create(&id1,0,task,&x);//创建线程

     13     pthread_join(id1,0);//主线程等待id1的结束

     14     printf("x=%d\n",x);

     15     return 0;

     16 }

 

      1 #include<stdio.h>

      2 #include<stdlib.h>

      3 #include<pthread.h>

      4 

      5 void* task(void* p){

      6   printf("%d\n",(int)p);

      7 

      8 }

      9 void* task2(void* p){

     10   sleep(1);

     11   int* pi = p;

     12   printf("*pi=%d\n",*pi);

     13 }

     14 int main(){

     15   pthread_t id;

     16   int x = 100;

     17   pthread_create(&id,0,task,(void*)x);

     18   pthread_join(id,0);

     19   int* pi = malloc(4);

     20   *pi=300;

     21   pthread_create(&id,0,task2,pi);

     22   free(pi);//free()释放掉了pi,使pi无效,传入task2将无效

     23   pthread_join(id,0);

     24   return 0;

     25  }

 

 线程的取消--线程可以被其他线程取消,有一套API(了解)

      1 #include <stdio.h>

      2 #include <pthread.h>

      3 void* task1(void *p){//线程2取消线程1

      4   //设置可以取消

      5   pthread_setcancelstate(//换成DISABLE不能取消

      6     PTHREAD_CANCEL_ENABLE,0);

      7   //设置取消方式: 立即/下一个取消点

      8   pthread_setcanceltype(//立即,DEFERRED下一个

      9    PTHREAD_CANCEL_DEFERRED/*ASYNCHRONOUS*/,0);

     10   while(1) {

     11     printf("I am superman!\n");

     12     usleep(1); } }

     13 void* task2(void *p){

     14   sleep(3); printf("开始取消线程1\n");

     15   pthread_cancel(*(pthread_t*)p);  }

     16 int main(){

     17   pthread_t id1,id2;

     18   pthread_create(&id1,0,task1,0);

     19   pthread_create(&id2,0,task2,&id1);

     20   pthread_join(id1,0); pthread_join(id2,0);

     21   return 0;

    22 }

     

线程同步--因为多线程之间共享进程的资源,多个线程同时访问相同的资源时需要互相协调,以防止数据出现不一致、不完整的问题,线程之间的协调和通信叫线程同步。

 线程同步有很多解决方案:

  线程同步的思路:访问共享资源时,不能并行,而是串行(一个一个的访问)

  pthread中,提供了互斥量(互斥锁)实现线程同步。

  互斥量的使用步骤:

   1 定义互斥量 pthread_mutex_t lock;

   2 初始化互斥量,方法有二:

     pthread_mutex_init(&lock,0);

     或者在定义的时候用宏同时赋值:

     pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;

   3 加上互斥锁(给访问共享资源的代码加)

     pthread_mutex_lock(&lock) 

   4 访问

   5 解锁  pthread_mutex_unlock(&lock)

   6 释放互斥锁的资源

     pthread_mutex_destroy(&lock)

互斥量实例:

      1 #include<stdio.h>

      2 #include<pthread.h>

      3 

      4 char* data[5];//存数据

      5 int size = 0; //人数

      6 

      7 pthread_mutex_t lock; //1 定义

      8 //= PTHREAD_MUTEX_INITIALIZER;

      9 

     10 void* task(void* p){

     11  pthread_mutex_lock(&lock);//3 上锁

     12  data[size] = (char*)p;

     13  usleep(100000);

     14  size++;

     15  pthread_mutex_unlock(&lock);//4 解锁

     16 }

     17 

     18 int main(){

     19   pthread_mutex_init(&lock,0);//2 初始化

     20   data[size] = "zhangfei";

     21   size++;

     22   pthread_t id1,id2;

     23   pthread_create(&id1,0,task,"guanyu");

     24   pthread_create(&id2,0,task,"zhaoyun");

     25   pthread_join(id1,0);

     26   pthread_join(id2,0);

     27   int i;

     28   for(i=0;i<size;i++){

     29    printf("%s\n",data[i]);

     30   }

     31   pthread_mutex_destroy(&lock);

     32   return 0;

     33 }

信号量-- 就是一个计数器,控制同时访问共享资源的线程/进程数量。

 如果信号量为1,效果等同于互斥量。

 信号量的使用,已经有了固定的API,步骤:

 1 定义一个信号量(semaphore)

   sem_t sem;

  信号量不属于pthread.h

 2 初始化信号量

   sem_init(&sem,0,最大值);

  第二个参数必须是0,0代表控制线程,其他值代表控制进程,Linux只支持线程。

  第三个参数就是计数的初始值,为1信号量的作用等价于互斥量。

 3 获取一个信号量(信号量减1)

   sem_wait(&sem);

 4 访问共享资源

 5 释放一个信号量(信号量加1)

   sem_post(&sem)

 6 如果不再使用,可以删除信号量

   sem_destroy(&sem)

信号量实现线程同步实例:

      1 #include<stdio.h>

      2 #include<pthread.h>

      3 #include<semaphore.h>

      4 

      5 char* data[5];//存数据

      6 int size = 0; //人数

      7 

      8 sem_t sem; //1 定义

      9 

     10 void* task(void* p){

     11  sem_wait(&sem);//3 上锁

     12  data[size] = (char*)p;

     13  usleep(100000);

     14  size++;

     15  sem_post(&sem);//4 解锁

     16 }

     17 

     18 int main(){

     19   sem_init(&sem,0,1);//2 初始化

     20   data[size] = "zhangfei";

     21   size++;

     22   pthread_t id1,id2;

     23   pthread_create(&id1,0,task,"guanyu");

     24   pthread_create(&id2,0,task,"zhaoyun");

     25   pthread_join(id1,0);

     26   pthread_join(id2,0);

     27   int i;

     28   for(i=0;i<size;i++){

     29    printf("%s\n",data[i]);

     30   }

     31   sem_destroy(&sem);

     32   return 0;

     33 }

练习:

 控制访问数据库的最大的并行线程数量

 数据库最多运行10个线程同时访问,启动20个线程,看一下信号量控制效果 

      1 #include<stdio.h>

      2 #include<pthread.h>

      3 #include<semaphore.h>

      4 

      5 sem_t sem;

      6 void* task(void* p){

      7     int i =(int)p;

      8     printf("%d个线程启动,申请连接\n",i);

      9     sem_wait(&sem);

     10     printf("%d个线程申请成功\n",i);

     11     sleep(10);

     12     printf("%d个线程完成连接\n",i);

     13     sem_post(&sem);

     14 }

     15 int main(){

     16     sem_init(&sem,0,10);//初始化

     17     int i;

     18     for(i = 0;i<20;i++){

     19        pthread_t id;

     20        pthread_create(&id,0,task,(void*)(i+1));

     21     }

     22     while(1);

     23 //  sem_destroy(&sem);

     24     return 0;

     25 }

 

死锁:线程之间互相锁定,导致所有线程都无法运行。多线程一定要避免锁死

 避免死锁的经验:

  顺序上锁,反向解锁,不要回调。

 thread1:

 lock(&mutex1);

 ...//1

 lock(&mutex2);//3  thread1阻塞等待mutex2

 ...

 unlock(&mutex2);

 ...

 unlock(&mutex1);

 

 thread2:

 lock(&mutex2);//2

 ...//4

 lock(&mutex1);//5 thread2阻塞等待mutex1

 ...

 unlock(&mutex1);

 ...

 unlock(&mutex2);

thread1运行到lock(&mutex2)时,如果thread2已经运行了lock(&mutex2)则thread1将阻塞等待thread2thread2运行到lock(&mutex1);时,因为thread1已经运行了lock(&mutex1);thread2将阻塞等待thread1,结构thread1thread2都将永远等待对方,无法结束,形成死锁

 

 

 

 

posted @ 2013-08-14 23:19  我没有领悟  阅读(500)  评论(0编辑  收藏  举报