现在的位置: 首页 > 综合 > 正文

linux 多线程编程

2013年08月06日 ⁄ 综合 ⁄ 共 6996字 ⁄ 字号 评论关闭

进程与线程

        进程是程序执行时的一个实例,是执行程序在一定数据集上运行的过程,是LINUX系统分配资源的基本单位。 

        线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),一个线程可以创建和撤销另一个线程

进程是资源管理的最小单位,线程是程序执行的最小单位。

Linux线程函数

        1.创建线程:pthread_create()

      头文件:#include
<pthread.h>

       函数原型:  int pthread_create(pthread_t *tid,const pthread_attr_t
*attr,void *(*start_routine)(void *),void *arg);

       函数参数:  pthread_t *tid:存放线程ID的指针

                       pthread_attr_t *attr:设置线程属性的结构体,通常为NULL。

                       void*(*start_routine)(void *):线程执行函数。

                       void *arg:线程执行函数参数。

       返回值:   成功返回0.

        2.线程属性初始化:pthread_attr_init()

        pthread_attr_init 函数初始化线程属性对象attr,填充所有属性为默认值。这个函数必须在pthread_create函数之前调用,做为pthread_create 函数的第2个参数。属性对象主要包括是否绑定、是否分离、堆栈地址、堆栈大小、优先级。默认的属性为非绑定、非分离、缺省1M的堆栈、与父进程同样级别的优先级。

         函数原型:int pthread_attr_init(pthread_attr_t *attr);

         函数参数: attr:线程属性变量。

         返回值:      成功返回0.

   线程属性 pthread_attr_t结构如下:

          typedef struct

          {

 
               
int                               detachstate;   
线程的分离状态

 
               
int                               schedpolicy;  
线程调度策略

 
              
struct sched_param              schedparam;  
线程的调度参数

 
              
int                               inheritsched;  
线程的继承性

 
              
int                                scope;       
线程的作用域

 
              
size_t                           guardsize;   
线程栈末尾的警戒缓冲区大小

 
              
int                                stackaddr_set;

  
            
void *                          stackaddr;   
线程栈的位置

   
           
size_t                           stacksize;    
线程栈的大小

           }pthread_attr_t;

     __detachstate,表示新线程是否与进程中其他线程脱离同步,如果置位则新线程不能用pthread_join()来同步,且在退出时自行释放所占用的资源。缺省为PTHREAD_CREATE_JOINABLE状态。这个属性也可以在线程创建并运行以后用pthread_detach()来设置,而一旦设置为PTHREAD_CREATE_DETACH状态(不论是创建时设置还是运行时设置)则不能再恢复到
PTHREAD_CREATE_JOINABLE状态。

    __schedpolicy,表示新线程的调度策略,主要包括SCHED_OTHER(正常、非实时)、SCHED_RR(实时、轮转法)和 SCHED_FIFO(实时、先入先出)三种,缺省为SCHED_OTHER,后两种调度策略仅对超级用户有效。运行时可以用过
pthread_setschedparam()来改变。

   __schedparam,一个struct sched_param结构,目前仅有一个sched_priority整型变量表示线程的运行优先级。这个参数仅当调度策略为实时(即SCHED_RR 或SCHED_FIFO)时才有效,并可以在运行时通过pthread_setschedparam()函数来改变,缺省为0。

    __inheritsched,有两种值可供选择:PTHREAD_EXPLICIT_SCHED和PTHREAD_INHERIT_SCHED,前者表示新线程使用显式指定调度策略和调度参数(即attr中的值),而后者表示继承调用者线程的值。缺省为PTHREAD_EXPLICIT_SCHED。

   __scope,表示线程间竞争CPU的范围,也就是说线程优先级的有效范围。POSIX的标准中定义了两个值: PTHREAD_SCOPE_SYSTEM和PTHREAD_SCOPE_PROCESS,前者表示与系统中所有线程一起竞争CPU时间,后者表示仅与同进程中的线程竞争CPU。目前LinuxThreads仅实现了PTHREAD_SCOPE_SYSTEM一值。


        3.线程函数去初始化:pthread_attr_destroy()

          如果要去除对pthread_attr_t结构的初始化,可以调用pthread_attr_destroy函数。如果pthread_attr_init实现时为属性对象分配了动态内存空间,pthread_attr_destroy还会用无效的值初始化属性对象,因此如果经pthread_attr_destroy去除初始化之后的pthread_attr_t结构被pthread_create函数调用,将会导致其返回错误。

         函数原型:int pthread_attr_destroy(pthread_attr_t *attr);

         函数参数:attr:线程属性变量。

         返回值:    成功返回0.


         4.结束线程:pthread_exit()

         函数原型:void pthread_exit(void *status);

         函数参数:void *status:线程结束后的返回值。

pthread_exit()与return

             理论上说,pthread_exit()和线程宿体函数退出的功能是相同的,函数结束时会在内部自动调用pthread_exit()来清理线程相关的资源。但实际上二者由于编译器的处理有很大的不同。在进程主函数(main())中调用pthread_exit(),只会使主函数所在的线程(可以说是进程的主线程)退出;而如果是return,编译器将使其调用进程退出的代码(如_exit()),从而导致进程及其所有线程结束运行。其次,在线程宿主函数中主动调用return,如果return语句包含在pthread_cleanup_push()/pthread_cleanup_pop()对中,则不会引起清理函数的执行,反而会导致segment
fault。


     
   5.等待线程结束:
pthread_join()

          函数原型:int pthread_join(pthread_t tid,void **status);

          函数参数:pthread_t tid:要等待结束的线程ID(当前进程中非分离的线程)。void **status用于存放线程的退出状态。

          返回值:    成功返回0.


     
   6.分离线程:
pthread_detach()

          函数原型:int pthread_detach(pthread_t tid);

          函数参数:pthread_t tid需要设置的线程ID。

        将非分离的线程设置为分离线程。即通知线程库在指定的线程终止时回收线程占用的内存等资源。在一个线程上使用多次pthread_detach的结果是不可预见的。

         7.设置线程的属性(优先级和调用策略):pthread_setschedparam()

          函数原型: int pthread_setschedparam(pthread_t target_thread,int policy,const sturct sched_param *param);

          函数参数: target_thread :线程ID。

                             policy:设置确定哪种线程调度策略:SCHED_OTHER(默认)、SCHED_RR、SCHED_FIFO

                             param:是struct sched_param类型指针,它包含成员变量sched_priority,指明要设置的线程优先级。                           

           得到线程属性参数用 pthread_attr_getschedparam(pthread_t target_thread,int policy,sturct sched_param *param);

         三种调度策略:

        SCHED_OTHER

         它是默认的线程分时调度策略,所有的线程的优先级别都是0,线程的调度是通过分时来完成的。简单地说,如果系统使用这种调度策略,程序将无法设置线程的优先级。请注意,这种调度策略也是抢占式的,当高优先级的线程准备运行的时候,当前线程将被抢占并进入等待队列。这种调度策略仅仅决定线程在可运行线程队列中的具有相同优先级的线程的运行次序。

       SCHED_FIFO

        它是一种实时的先进先出调用策略,且只能在超级用户下运行。这种调用策略仅仅被使用于优先级大于0的线程。它意味着,使用SCHED_FIFO的可运行线程将一直抢占使用SCHED_OTHER的运行线程J。此外SCHED_FIFO是一个非分时的简单调度策略,当一个线程变成可运行状态,它将被追加到对应优先级队列的尾部((POSIX 1003.1)。当所有高优先级的线程终止或者阻塞时,它将被运行。对于相同优先级别的线程,按照简单的先进先运行的规则运行。我们考虑一种很坏的情况,如果有若干相同优先级的线程等待执行,然而最早执行的线程无终止或者阻塞动作,那么其他线程是无法执行的,除非当前线程调用如pthread_yield之类的函数,所以在使用SCHED_FIFO的时候要小心处理相同级别线程的动作。

       SCHED_RR

       鉴于SCHED_FIFO调度策略的一些缺点,SCHED_RR对SCHED_FIFO做出了一些增强功能。从实质上看,它还是SCHED_FIFO调用策略。它使用最大运行时间来限制当前进程的运行,当运行时间大于等于最大运行时间的时候,当前线程将被切换并放置于相同优先级队列的最后。这样做的好处是其他具有相同级别的线程能在“自私“线程下执行。

         8.终止线程:pthread_cancel()

          函数原型:int pthread_cancel(pthread_t thread);

          函数参数:pthread_t thread:要终止的线程ID。

          返回值:    成功返回0.


        9.使能cancel状态:pthread_setcancelstate()

           允许/禁止线程被pthread_cancel终止.

          函数原型:int pthread_setcancelstate(int state,int *oldstate);

          函数参数: int state:要设置的新的状态,

                        PTHREAD_CANCEL_ENABLE 可以被终止的(默认),

                        PTHREAD_CANCEL_DISABLE 不可被终止的,

               int *oldstate:用于保存线程以前的状态。

       返回值:成功返回0.


       10.设置退出类型:pthread_setcanceltype()

        函数原型:int pthread_setcanceltype(int type,int *oldtype);

            函数参数:int type:要设置的类型。

                PTHREAD_CANCEL_DEFERRED 延迟终止请求,通过pthread_testcancel()设置取消点。

                PTHREAD_CANCEL_ASYNCHRONOUS 可以在任何时间被终止,

                           oldtype:保存以前的类型。

          返回值:成功返回0.


线程同步与互斥

           线程的同步, 发生在多个线程共享相同内存的时候, 这时要保证每个线程在每个时刻看到的共享数据是一致的。

线程同步有三种常用的机制: 互斥量(mutex), 读写锁(rwlock)和条件变量(cond).


   
一.互斥量

           互斥量从本质上说就是一把锁, 提供对共享资源的保护访问.

        1.初始化互斥锁

         函数原型:int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restric attr);

         函数参数:mutex:要初始化的互斥锁指针。attr:指向属性对象的指针,该属性对象定义要初始化的互斥锁的属性。

        返回值: 成功返回0.

       pthread_mutex_init() 函数是以动态方式创建互斥锁的,参数attr指定了新建互斥锁的属性。如果参数attr为空,则使用默认的互斥锁属性,默认属性为快速互斥锁
。互斥锁的属性在创建锁的时候指定,在LinuxThreads实现中仅有一个锁类型属性,不同的锁类型在试图对一个已经被锁定的互斥锁加锁时表现不同。

此外,还可以用宏 PTHREAD_MUTEX_INITIALIZER 来初始化静态分配的互斥锁,如下:

                                       
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
;

       互斥锁的属性在创建锁的时候指定,在LinuxThreads实现中仅有一个锁类型属性,不同的锁类型在试图对一个已经被锁定的互斥锁加锁时表现不同。当前(glibc2.2.3,linuxthreads0.9)有四个值可供选择:

  • PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。
  • PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。
  • PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。
  • PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。

        2.初始化互斥锁属性对象

        函数原型:int pthread_mutexattr_init(pthread_muexattr_t *mattr)

        函数参数:mattr:取值PTHREAD_PROCESS_PRIVATE(缺省)

                                                  PTHREAD_PROCESS_SHARED

        返回值:成功返回0.


        3.互斥锁操作_加锁:

        函数原型:int pthread_mutex_lock(pthread_mutex_t *mutex);  //堵塞等待

                            int pthread_mutex_trylock(pthread_mutex_t *mutex);  //非堵塞锁

        4.互斥锁操作_解锁

         函数原型:int pthread_mutex_unlock(pthread_mutex_t *mutex);

        5.销毁互斥锁:

         函数原型:int pthread_mutex_destroy(pthread_mutex_t *mutex);


     二.读写锁

一次只有一个线程可以占有写模式的读写锁, 但是可以有多个线程同时占有读模式的读写锁.

  • 当读写锁是写加锁状态时, 在这个锁被解锁之前,

抱歉!评论已关闭.