Unix C (十一)

来源:互联网 发布:网络大电影的发展趋势 编辑:程序博客网 时间:2024/05/18 02:40

线程
  1、理论和原理
  1)主流的操作系统都是支持多进程的,每个进程的内部可以启动多线程完成代码的并行;每个线程的内部可以无限启动多线程。
  2)线程是轻量级的,不拥有自己独立的内存空间,共享所在进程的相关资源(代码区/全局区/堆/文件状态/共享资源),线程拥有的只是一个独立的栈空间。
  3)进程是重量级的,必须拥有自己独立的内存空间。
  4)计算机执行代码必备CPU/内存,多线程的执行也是。
  5)内存可分,CPU不可分。多线程可以并行,多线程的目的其实就是要多个线程并行执行一段代码。
  6)0.1秒分成100个CUP时间片,每个1毫秒,这样当我们看到代码运行的结果时,每个线程都有运行,因此造成了并行的效果,但没有真正的并行。
  7)多线程是利用CPU时间片实现的一种假的并行,但还是可以大幅提升cpu的利用效率,因此说线程是并行的
  8)进程中可以有多个线程,但其中必须有一个是主线程,每个进程都必须有一个主线程,就是main函数。
    9)线程内部是顺序执行代码,但多线程之间是乱序执行。线程之间互相影响,但不互相依赖。
   
  2 、编码
  1)UC中,线程开发需要头文件pthread.h和遵循POSIX规范。
  2)对应的库文件 libpthread.so,gcc时需要带上库。
  3)线程相关函数基本都是以pthread_开头。
  4)创建线程的函数:
  int pthread_create(pthread_t* threadid,属性指针,void* (*fun) (void *),void*p)
   threadid 存储线程id
    属性指针一般给NULL
   fun 是函数指针,线程执行的代码写在函数中
   p 是传给函数的参数,没有就给NULL
  返回 0代表成功,失败会返回错误码
  注:线程这里不使用errno和perror
  5)主线程结束,导致进程结束,进程结束,导致所有线程的结束。
   6)pthread_join  函数 可以让一个线程等待另外一个线程的结束,并取得该线程的返回值。
   7)结束线程用return或pthread_exit,可能需要作类型转换,否则警告。返回值可以用pthread_join取得。
   8)线程有分离状态和非分离状态,分离状态的线程资源回收无需等待其他线程,因此分离状态的线程join无效。pthread_detach 可以把线程变成分离状态。
   9)线程可以被其他线程取消,pthread_cancel可以取消线程的运行,需要用pthread_setcancelstate设置是否支持取消,用pthread_setcanceltype设置取消的类型(立即还是延后).

  3、线程(thread) - 做应用,有网络必有线程。
    1)多线程之间互相独立,又互相影响。
    2)多线程可以大幅提升代码的效率。
    3)程序的运行必须拥有CPU和内存,内存可分,  CPU不可分,如何实现并行。大多数的操作系统都是采用CPU时间片实现CPU的在多线程之间的轮换。CPU时间片是极短的一段CPU的执行时间,拥有CPU时间片的线程有机会运行。
       比如:人的感官是需要时间的,比如视觉0.1秒。就是100毫秒。假定CPU时间片是1毫秒,有4个线程。每个线程先分一个时间片,也就是1毫秒的CPU运行时间。每个线程只能运行1毫秒,时间片的运行时间到了以后就只能看其他线程运行,直到所有线程的时间片都运行完毕,再重新分配。
  4)针对时间点的并行是不存在的,针对时间段的并行就是我们通常说的代码并行。
    5)每个进程都有一个主线程,就是main()函数,主线程结束,进程也结束,同时导致所有线程都结束。
   线程的编程: 
   Unix/Linux的线程相关函数都在pthread.h中,代码都在libpthread.so中。线程相关的函数/结构都以pthread_ 开头。比如创建线程函数:pthread_create();
  int pthread_create(pthread_t* id,pthread_attr_t* attr, void* (*fa)(void*),void* arg)
    pthread_create()是一个四针函数,参数id就是用于存储线程ID的;attr是线程的属性,一般给0即可(默认属性); fa是一个函数指针,写线程执行的代码;arg是传给fa的参数。fa+arg指定了线程要执行的代码。
    返回值: 成功返回0,失败返回错误码,想看错误信息需要用strerror()做转换。
    6)每个线程启动以后,只能执行一个函数,主线程执行的是main(),其他线程执行自定义的一个函数。这个函数以并行的方式运行。
    7)线程之间的代码乱序执行,每个线程的内部代码都是顺序执行。每个线程都会返回自己的错误码,而不是使用errno。
    8)pthread_join()函数可以让一个线程等待另外一个线程的结束,并取得线程的返回值。
  9)如果在线程a中调用了pthread_join(b,0),线程a就会等待线程b的结束,等线程b结束以后a才能继续运行。
  10)线程传参时,一定要注意保证地址的有效性,尤其是堆内存。支持直接传递int。
    关于函数的返回:
    1 能返回局部变量,但不能返回指向局部变量的指针。
    2 static的局部变量的地址可以返回(全局区)。
    3 数组理论上可以做返回值,但返回值类型不能写数组。最好用指针。int[] get() 错.

  void fa(int* pi){ *pi = 200;}
  int main(){
     int x;
      fa(&x); -> pi = &x ->*pi = x = 200;
  }


  void fa(int** pi){ *pi = 地址1;}
  int main(){
    int* px;
    fa(&px); ->pi = &px ->*pi = px = 地址1;
  }


 4、 线程的状态:
    线程应该处于以下两种状态:
    1 分离状态
      就是线程一旦结束,不用管其他线程,直接回收资源。函数pthread_detach()设置线程分离状态。
    2 join状态
      如果线程用pthread_join(),就处于join()状态,就是线程结束时暂不回收资源,到pthread_join()函数结束时再回收资源。
    注:没有分离也没有join()的线程资源回收是没有保障的。
    分离状态的线程再调用pthread_join()没有效果   
5、线程的退出
    正常退出:
    在线程的函数中执行了return语句。
    执行了pthread_exit(void*)函数
    非正常退出:
    自身出现错误
    被其他线程终止/取消


   exit()和pthread_exit(void*)的区别?
     exit() 是结束进程,所有线程全结束
     pthread_exit()是结束线程,其他线程继续运行
    
   参数void* 和 return 一样,都是 用于返回值。
   取消线程的函数: pthread_cancel()


  多线程之间是共享进程的资源,因此有可能出现共享数据的冲突,解决方案就是把并行访问改为串行访问,这种技术叫线程同步。线程同步的技术包括: 互斥量、信号量、条件变量。
   互斥量又叫互斥锁,是线程在设计时官方的同步技术,编程步骤如下:
   1 声明互斥量 
    pthread_mutex_t lock; //变量名不一定叫lock
   2 初始化互斥量
    pthread_mutex_init(&lock); 或在声明的同时赋值: pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
   3 加锁/上锁    pthread_mutex_lock(&lock);
   4 执行共享数据的访问代码
   5 解锁    pthread_mutex_unlock(&lock);
   6 释放锁的资源
      pthread_mutex_destroy(&lock);


  信号量是一个计数器,用于控制访问共享资源的最大的并行 进程/线程的数量。
  信号量的工作原理:先设置最大值最初始计数,每上来一个计数减1,每退出一个就加1,到0就不允许新进程/线程访问,除非计数又回到大于0。
  信号量不属于线程的范围,不在pthread.h中,只是一个线程计数辅助。头文件 semaphore.h
  信号量如果初始计数为1,效果等同于互斥量。
  信号量的编程步骤:
   1 声明信号量  sem_t sem;
   2 初始化信号量的原始计数 sem_init()
    sem_init(&sem,0,count)
   第一个参数就是信号量的地址
   第二个参数必须是0,0代表线程的计数,非0代表进程的计数(Linux系统没有提供进程计数功能)。
   第三个参数就是 计数的初始值(最大计数)。
   3 计数减1    sem_wait(&sem);
   4 正常使用
   5 计数加1    sem_post(&sem);
   6 释放信号量资源    sem_destroy(&sem);
  
 使用线程同步技术,小心避免死锁。
  pthread_mutex_t lock1,lock2;
  线程a:
   lock(&lock1);
   ...
    lock(&lock2); //等待线程b unlock(&lock2)
    ...
    unlock(&lock2);
   unlock(&lock1);  
   线程b:
   lock(&lock2);
   ...
    lock(&lock1);//等待线程a unlock(&lock1)
    ...
    unlock(&lock1);
   unlock(&lock2);  

结果就是看起来都问题,但执行 a和b互相锁定,死锁。


实例:

(1)

#include <stdio.h>
#include <pthread.h>
#include <string.h>
void * task1(void *p){
  int i;
  for(i=0;i<10;i++) {
printf("%d\n",i);
usleep(100000);
  }
}
void * task2(void *p){
  char c;
  for(c='a';c<'k';c++) {
printf("%c\n",c);
usleep(100000);
  }
}
int main(){
  pthread_t id1,id2;
  int res = pthread_create(&id1,0,task1,NULL);
  if(res) printf("create:%s\n",strerror(res));
  res = pthread_create(&id2,0,task2,NULL);
  if(res) printf("create:%s\n",strerror(res));
  sleep(2);
  return 0;
}

(2)

#include <stdio.h>
#include <pthread.h>
#include <string.h>


char* get(){
  char * str = "abcd";
  //char str[] = "abcd";// 栈区复制一份
  return str;
}
void* task(void *p){
  strcpy(p,"12345");
  return "WORLD";
}
void* task2(void *p){
  int *pi = p;
  int i;
  static int sum = 0;
  for(i=1;i<=*pi;i++){
    sum = sum+i;
  }
  return &sum;
}
int main(){
  //char *s = get(); printf("s=%s\n",s);
  pthread_t id1;
  char str[] = "hello";
  pthread_create(&id1,NULL,task,str);
  char *res;//join+一级指针变量可以取返回
  pthread_join(id1,(void**)&res);
  printf("str=%s,return=%s\n",str,res);
  int num = 10;
  pthread_create(&id1,NULL,task2,&num);
  int *pi;
  pthread_join(id1,(void**)&pi);
  printf("sum=%d\n",*pi);
}

(3)

#include <stdio.h>
#include <pthread.h>
void* task(void *p){
  //设置是否开启取消
  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0);
//pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,0);
  pthread_setcanceltype(//立即退出
PTHREAD_CANCEL_ASYNCHRONOUS,0);
  //pthread_setcanceltype(//下一个取消点退出
//PTHREAD_CANCEL_DEFERED,0);
  while(1) { printf("-----------\n"); usleep(1); }}
void* task2(void *p){
  usleep(100000); printf("开始取消线程1....\n");
  pthread_cancel(*(pthread_t*)p); }
int main(){
  pthread_t id1,id2;
  pthread_create(&id1,0,task,0);
  pthread_create(&id2,0,task2,&id1);
  pthread_join(id1,0); pthread_join(id2,0);
}





















0 0