线程--具体函数介绍

来源:互联网 发布:鸟哥php 编辑:程序博客网 时间:2024/06/05 00:25

线程的概念

  1. 线程的优点

  线程的实现时间远晚于进程.最早实现是solaris 上线程,多线程技术已经被许多操作系统所支持,包括Windows/NT,和Linux 使用多线程的理由之一是和进程相比,它是一种非常”节俭”的多任务操作方式。

  启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种”昂贵”的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,.– 线程间方便的通信机制 ,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。多线程的程序会提高响应速度.特别是GUI使多CPU系统更加有效。操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上。改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。

  2. Linux 对线程的支持

  Linux系统下的多线程遵循POSIX线程接口 ,所以被称为pthread.

  pthread是目前Linux平台上使用最为广泛的线程库,由Xavier Leroy 负责开发完成,并已绑定在GLIBC中发行。

  Linux内核并不支持真正意义上的线程,LinuxThreads是用与普通进程具有同样内核调度视图的轻量级进程来实现线程支持的。这些轻量级进程拥有独立的进程id,在进程调度、信号处理、IO等方面享有与普通进程一样的能力。因此一个多线程程序用ps查看,可以看到每一个线程也占用一行显行LinuxThreads 项目最初将多线程的概念引入了 Linux,但是LinuxThreads 并不完全遵守 POSIX 线程标准。一个更新的Native POSIX Thread Library(NPTL)正在应用开来.由于历史原因.有大量应用采用pthread,很多嵌入式平台也只是支持pthread库.

  3. 线程与进程区别

  进程是一个应用程序独立运行单位,而线程不能独立存在,必须由在一个进程创建.在Windows下,线程和进程都是内核内置的机制,而Linux一开始就在内核创建进程机制,直到2.2后才开始加入线程实现,到2.6才稳定Linux的线程库是基于应用库实现,远没有基于内核的进程稳定.在重负衡的关键任务的服务器,往往采用多进程机制实现.

  进程空间

  每个进程都独立占有4G的独立虚拟内存空间,各自拥有完整的堆,栈,代码段和数据段在调用fork时,子进程完全从父进程4G空间复制过来这意味,现有堆栈的局域变量,全局变量等的值完全一样在fork后,父子进程各自独立修改自己空间的变量两者即便是有同名变量,各自值也是独立修改进程间,包括父子进程之间交换数据必须需要进程间通讯机制来实现

  线程空间

  同一个进程创建的所有线程之间共享创建进程的空间,即大家拥有相同2G空间,当一个线程修改一个全局变量后,另一个线程也访问得到这个值因此在多线程并发修改某个值,必须要进行加锁保护,以便能正确修改值,Linux 线程基本编程

  Linux 线程的基本函数

  l 常用线程函数

  – pthread_create 创建一个线程

  – pthead_exit 线程自行退出

  – pthread_join 其它线程等待某一个线程退出

  – pthread_cancel 其它线程强行杀死某一个线程

  pthread线程库的使用

  – glib库内置了线程库.

  – 在源码中使用头文件 pthread.h

  – 用gcc链接时加上 –lpthread 选项,链接线程库

  1)pthread_create 创建一个线程

  l 线程的创建

  – int pthread_create(pthread_t *thread,pthread_attr_t *attr,

  void (*start_routine)(void ), void *arg);

  pthread_create创建一个线程,thread是用来表明创建线程的ID,attr指出线程创建时候的属性,我们用NULL来表明使用缺省属性.start_routine函数指针是线程创建成功后开始执行的函数,arg是这个函数的唯一一个参数.表明传递给start_routine的参数.一个进程中的每个线程都由一个线程ID(thread ID)标识,其数据类型是pthread_t(常常是unsigned int)。如果新的线程创建成功,其ID将通过thread指针返回。

  2)pthread_exit 退出一个线程

  线程的退出有两种方式,一种线程函数运行结束,比如到函数结尾或用return退出.线程自然结束.这是最常用的方式.

  调用pthread_exit退出.

  – void pthread_exit(void *retval);

  退出当前线程,并且设线程的返回值为retval

  返回值可以用pthread_join取得

  pthread_join 等待线程退出

  int pthread_join(pthread *thread,void **thread_return);

  这个函数类似于waitpid,pthread_join是当前线程在等待另一个线程结束,只不过前者是在等待一个进程退出,后者在等待一个线程编号为thread的线程退出.当一个线程调用pthread_exit时,如果其它线程或进程使用了pthead_join在等待这个线程结束,那线程ID和退出状态将一直保留到pthread_join执行时.

pthread_exit()设定的返回值,或者return的返回值可以被pthread_join的

  thread_return捕获

  4)pthread_cancel 杀死一个线程

  pthread_cancel(pthread_t thread) ;

  当前线程将杀死线程ID为thread的线程

  pthread_exit()是当前线程自已退出,而pthread_cancel是其它线程杀死别的线程

  2. 关于线程的生存周期

  程序的主进程默认为主线程.

  一个子线程的生存周期由pthread_create 创建后开始,

  一直到线程函数执行完毕,或者执行到pthread_exit 处.线程的生命到此结束.

  其它线程可以用pthread_cancel强制杀死另一线程.

  主线程退出,(比如在主函数里调用exit),它的子线程无论是否执行完毕,都会随主线程退出

  而一同消失.

  所以在一般在主函数里,必须要判断的一下子线程是否真正退出,如是方可以把主线程退出.否则很可能造成程序结果不正确

  判断线程是否结束最简单的方法是在主线程的退出用 pthread_join来等待子线程退出即可线程示例

  #include

  #include <pthread.h>  #include <sched.h>  pthread_attr_t attr;  pthread_t tid;  struct sched_param param;  int newprio=20;  pthread_attr_init(&attr);  pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);  pthread_attr_getschedparam(&attr, &param);  param.sched_priority=newprio; //设置优先级  pthread_attr_setschedparam(&attr, &param);  pthread_create(&tid, &attr, (void *)myfunction, myarg);  for(i=0;i<RUN_TIME;i++)  {  printf("This is the main process.\n");  sleep(1);  }  pthread_join(id,NULL);  return (0);  }

  扫描一个目录,把这一个目录下所有文件名加上*.bak复制一次

  这一个实例为每一个文件创建一个线程进行拷贝没有做优化.因此文件较多的情况下,线程可能较多每个程序在内部使用read,write来处理文件

   pthread_mutex_lock() 和 pthread_mutex_unlock()
在线程程序中这些调用执行了不可或缺的功能。他们提供了一种 相互排斥的方法(互斥对象即由此得名)。两个线程不能同时对同一个互斥对象加锁。
互斥对象是这样工作的。如果线程 a 试图锁定一个互斥对象,而此时线程 b 已锁定了同一个互斥对象时,线程 a 就将进入睡眠状态。一旦线程 b 释放了互斥对象(通过 pthread_mutex_unlock() 调用),线程 a 就能够锁定这个互斥对象(换句话说,线程 a 就将从 pthread_mutex_lock() 函数调用中返回,同时互斥对象被锁定)。同样地,当线程 a 正锁定互斥对象时,如果线程 c 试图锁定互斥对象的话,线程 c 也将临时进入睡眠状态。对已锁定的互斥对象上调用 pthread_mutex_lock() 的所有线程都将进入睡眠状态,这些睡眠的线程将“排队”访问这个互斥对象。
通常使用 pthread_mutex_lock() 和 pthread_mutex_unlock() 来保护数据结构。这就是说,通过线程的锁定和解锁,对于某一数据结构,确保某一时刻只能有一个线程能够访问它。可以推测到,当线程试图锁定一个未加锁的互斥对象时,POSIX 线程库将同意锁定,而不会使线程进入睡眠状态。

0 0
原创粉丝点击