Linux 多线程编程( POSIX )( 一 )-----> 基础篇

ATTETION:附加代码地址:   点击打开链接 http://blog.csdn.net/shanshanpt/article/details/7372708



1. 
      所谓线程就是“一个进程内部的一个控制序列”。也就是一个进程内部的并行的基础!

2.        Linux进程可以看成只有一个控制线程:
            一个进程在同一时刻只做一件事情。有了多个控制线程以后,
            在程序设计时可以把进程设计成在同一时刻能够做不止一件事,
            每个线程处理各只独立的任务。即所谓并行!
           
3.        线程的优点:
                  (1)通过为每种事件类型的处理分配单独的线程,能够简化处理异步时间的代码。
                  (2)多个线程可以自动共享相同的存储地址空间和文件描述符。
                  (3)有些问题可以通过将其分解从而改善整个程序的吞吐量。
                  (4)交互的程序可以通过使用多线程实现相应时间的改善,多线程可以把程序中
                              处理用户输入输出的部分与其它部分分开。

4.            线程的缺点:
                  线程也有不足之处。编写多线程程序需要更全面更深入的思考。
                  在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的
                  变量而造成不良影响的可能性是很大的。调试一个多线程程序也
                  比调试一个单线程程序困难得多。           
           
5.        线程标识:
            我们已经知道进程有进程ID就是pid_t,那么线程也是有自己的ID的pthread_t数据类型!
            注意:实现的时候可以用一个结构来代表pthread_t数据类型,所以可以移植的操作系统
                        不能把它作为整数处理。因此必须使用函数来对来对两个线程ID进行比较。
           
            >>>>>:           
            关于比较线程ID函数:
            #include<pthread.h>
            intpthread_equal(pthread_t tid1,pthread_t tid2 );
            注意:在线程中,线程ID的类型是pthread_t类型,由于在Linux下线程采用POSIX标准,
                          所以,在不同的系统下,pthread_t的类型是不同的,比如在ubuntn下,是unsigned long类型,
                          而在solaris系统中,是unsigned int类型。而在FreeBSD上才用的是结构题指针。
                          所以不能直接使用==判读,而应该使用pthread_equal来判断。
            返回:非0值->相等;0->不等价       
                       
            >>>>>:
            获取自身线程的id:
            #include<pthread.h>
            pthread_tpthread_self(void);                       
            返回:调用线程的线程id           
            注意:在使用pthread_create(pthread_t *thread_id,NULL,void* (*fun) (void*),void * args);
                          虽然第一个参数中已经保存了线程ID,但是,前提是主线程首先执行时,才能实现的,
                          而如果不是,那么thread指向一个未初始化的变量。那么子线程想使用时,应该使用pthread_self();           
                       
6.        线程的创建:
            #include<pthread.h>
            intpthread_create(pthread_t *restrict tidp,const pthread _attr_t*restrict attr,void *(*start_rtn)(void),void *restrictarg);                 
           
            参数:
                    第一个参数为指向线程标识符的指针。
                    第二个参数用来设置线程属性。
                    第三个参数是线程运行函数的起始地址。
                    最后一个参数是运行函数的参数
           
            返回成功时,由tidp指向的内存单元被设置为新创建线程的线程ID。attr参数用于制定各种不同的
            线程属性。新创建的线程从start_rtn函数的地址开始运行,该函数只有一个万能指针参数arg,如
            果需要向start_rtn函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个
            结构的地址作为arg的参数传入。             
                       
            注意:restrict修饰,
            pthread_join
            运行:
            在编译时注意加上-lpthread参数,以调用静态链接库。因为pthread并非Linux系统的默认库
            if不加上就会报错:undefined reference to `pthread_create'。
                       
7.        线程的终止与等待:
            线程是依进程而存在的,当进程终止时,线程也就终止了。当然也有在不终止整个进程的情况下停止它的控制流。
            <1>. 线程从启动例程中返回,返回值是线程的退出码。
              <2>. 线程可以被同一进程中的其他线程取消。
              <3>. 线程退出调用pthread_exit.
            #include<pthread.h>
            voidpthread_exit( void * rval_ptr);           
           
            rval_ptr是一个无类型指针,与传给启动例程的单个参数类似。
            进程中的其他线程可以调用pthread_join函数访问到这个指针。
           
            pthread_join:获得进程的终止状态。
           
            intpthread_join( pthread_t thread, void ** rval_ptr );
            若成功返回0,否则返回错误编号。
            调用pthread_join进程将一直阻塞,直到指定的线程调用pthread_exit,从启动例程中或者被取消。
            如果线程只是从它的启动历程返回,rval_ptr将包含返回码。
           
8.        pthread_detach:使线程进入分离状态
           
            intpthread_detach( pthread_t tid );
            若成功则返回0,否则返回错误编号。
            注意:默认情况下是pthread_join等待线程的结束,并可以获得结束时的状态!
                        如果线程已经处于分离状态,线程的底层存储资源可以在线程终止时立即被收回。
                        当线程被分离时,并不能用pthread_join函数等待它的终止状态。
           
9.        pthread_cancel:取消同一进程中的其他线程(注意是取消其他线程)
           
            intpthread_cancel(pthread_t tid);
            若成功返回0,否则返回错误编号。
            注意:pthread_cancel并不等待线程终止,它仅仅提出请求。是否真的执行还看目标线程的
                        state和type的设置了!
           
10.  pthread_cleanup_push 和pthread_cleanup_pop:线程清理处理程序           
           
            voidpthread_cleanup_push( void ( * rtn ) ( void * ),void *arg );
            voidpthread_cleanup_pop( int exe );
            参数:
                        rtn:  处理程序入口地址
                        arg:传递给处理函数的参数
            线程可以安排它退出时需要调用的函数,这样的函数称为线程清理处理程序,线程可以建立多个清理处理程序。
            注意:此处是使用栈保存的,所以是先进后处理原则!
                        如果线程是通过从启动例程中返回而终止的,它的处理程序就不会调用。
           
            !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            ATTENTION:
                                    pthread_cleanup_push注册一个回调函数,如果你的线程在对应的pthread_cleanup_pop
                                    之前异常退出(return是正常退出,其他是异常),那么系统就会执行这个回调函数(回调函
                                    数要做什么你自己决定)。但是如果在pthread_cleanup_pop之前没有异常退出,
                                    pthread_cleanup_pop就把对应的回调函数取消了,
           
            所以请注意:
                        只有在“清理函数”的设置和取消之间有异常的退出,才会调用我们设置的“清理函数”。
                        否则是不会输出的!
                        一般的触发条件是:在之间有pthread_exit(非正常退出);或者有取消点时候!
                       
            关于“取消点” (cancellation point ):
                        例如执行下面代码:
                                                      printf("sleep\n");
                                                        sleep(10);
                                                        printf("wake \n");
            在sleep函数中,线程睡眠,结果收到cancel信号,这时候线程从sleep中醒来,但是线程不会立刻退出。
            >>>>>:           
                        函数:pthread_testcancel():
                        描述:函数在运行的线程中创建一个取消点,如果cancellation无效则此函数不起作用。
                       
                        pthread的建议是:如果一个函数是阻塞的,那么你必须在这个函数前后建立 “ 取消点 ”, 比如:
                                                      printf("sleep\n");
                                                      pthread_testcancel();
                                                        sleep(10);
                                                        pthread_testcancel();
                                                        printf("wake \n");
                        在执行到pthread_testcancel的位置时,线程才可能响应cancel退出进程。
                       
                        对于一些函数来说本身就是有cancellation point 的,那么可以不管,但是大部分还是没有的,
                        所以要使用pthread_testcancel来设置一个取消点,那么也并不是对于所有的函数都是有效的,
                        对于有延时的函数才是有效的,更清楚的说是有时间让pthread_cancel响应才是OK的!
                       
            附加:
                        POSIX中的函数cancellation点的:
                        pthread_join
                        pthread_cond_wait
                        thread_cond_timewait
                        pthread_testcancel
                        sem_wait
                        sigwait              都是cancellation点.
                        下面的这些系统函数也是cancellation点:
                        accept
                        fcntl
                        open
                        read
                        write
                        lseek
                        close
                        send
                        sendmsg
                        sendto
                        connect
                        recv
                        recvfrom
                        recvmsg
                        system
                        tcdrain
                        fsync
                        msync
                        pause
                        wait
                        waitpid
                        nanosleep
 
                        其它的一些函数如果调用了上面的函数, 那么, 它们也是cancellation点.
                        intpthread_setcancelstate (int STATE, int *OLDSTATE);
                        用于允许或禁止处理cancellation,
                        STATE可以是:PTHREAD_CANCEL_ENABLE, PTHREAD_CANCEL_DISABLE

                        intpthread_setcanceltype (int TYPE, int *OLDTYPE);
                        设置如何处理cancellation, 异步的还是推迟的.
                        TYPE可以是:PTHREAD_CANCEL_ASYNCHRONOUS, PTHREAD_CANCEL_DEFERRED
                             
            >>>>                 
            摘录:http://blogt.chinaunix.net/space.php?uid=23381466&do=blog&id=58787

                        什么是取消点(cancelation point)?

                        资料中说,根据POSIX标准,pthread_join()、pthread_testcancel()、pthread_cond_wait()、
                        pthread_cond_timedwait()、sem_wait()、sigwait()等函数以及read()、write()等会引起阻塞
                        的系统调用都是Cancelation-point。而其他pthread函数都不会引起 Cancelation动作。但
                        是pthread_cancel的手册页声称,由于LinuxThread库与C库结合得不好,因而目前C库函
                        数都不是Cancelation-point;但CANCEL信号会使线程从阻塞的系统调用中退出,并置
                        EINTR错误码,因此可以在需要作为Cancelation-point的系统调用前后调用pthread_testcancel(),
                        从而达到POSIX标准所要求的目标,即如下代码段:

                        pthread_testcancel();
                        retcode =read(fd, buffer, length);
                        pthread_testcancel();

                        我发现,对于C库函数来说,几乎可以使线程挂起的函数都会响应CANCEL信号,终止线程,
                        包括sleep、delay等延时函数。           
                             
            !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
           
11.           
            #include<pthread.h>
            intpthread_setcancelstate( int state, int* oldstate );

            描述:
            pthread_setcancelstate()f函数设置线程取消状态为state,并且返回前一个取消点状态oldstate。
            取消点有如下状态值:
            PTHREAD_CANCEL_DISABLE:取消请求保持等待,默认值。
            PTHREAD_CANCEL_ENABLE:取消请求依据取消类型执行;参考pthread_setcanceltype()。
           
            参数:
            state:新取消状态。
            oldstate:指向本函数所存储的原取消状态的指针。

            ///     
     
            #include<pthread.h>
            intpthread_setcanceltype( int type, int* oldtype );

            描述:
            pthread_setcanceltype()函数设置运行线程的取消类型为type,并且返回原取消类型与oldtype。
           
            取消类型值:
    PTHREAD_CANCEL_ASYNCHRONOUS:如果取消有效,新的或者是等待的取消请求会立即执行。
    PTHREAD_CANCEL_DEFERRED:如果取消有效,在遇到下一个取消点之前,取消请求会保持等待,默认值。
   注意if没有取消点,那么陷入死锁!!!
            注意:标注POSIX和C库的调用不是异步取消安全地。

            参数:
            type:新取消类型
            oldtype:指向该函数所存储的原取消类型的指针。

     
12.    posix变量一次初始化问题:
            >>>>>:
            如果我们需要对一个posix变量静态的初始化,可使用的方法是用一个互斥量对该变量的初始话进行控制。
            但有时候我们需要对该变量进行动态初始化,pthread_once就会方便的多。
           
            pthread_once_t  once_control =PTHREAD_ONCE_INIT;
            intpthread_once( pthread_once_t *once_control, void( * init_routine )( void ) );
            参数:once_control                控制变量
                        init_routine                        初始化函数
            若成功返回0,若失败返回错误编号。
            pthread_once_t的变量是一个控制变量。控制变量必须使用PTHREAD_ONCE_INIT宏静态地初始化。
           
            pthread_once工作原理:
                                                      pthread_once函数首先检查控制变量,判断是否已经完成初始化,如果完成
                                                      就简单地返回;否则,pthread_once调用初始化函数,并且记录下初始化被完成。
                                                      如果在一个线程初始时,另外的线程调用pthread_once,则调用线程等待,直到
                                                      那个现成完成初始话返回。
             
13.      线程的私有数据:
            在进程内的所有线程共享相同的地址空间,任何声明为静态或外部的变量,或在进程堆声明的变量,
            都可以被进程所有的线程读写。那怎样才能使线程序拥有自己的私有数据呢。posix提供了一种方法,
            创建 " 线程键"。
            #include<pthread.h>
            intpthread_key_create(pthread_key *key,void(*destructor)(void*));
            参数:
            key                      私有数据键
            destructor      清理函数参数
            ifdestructor 不为空,那么系统将调用这个函数来释放绑定在这个键上的内存块。
           
            这个函数常和函数pthread_once一起使用,为了让这个键只被创建一次。
           
14.      关于: "线程存储 "      
            现在有一全局变量,所有线程都可以使用它,改变它的值。而如果每个线程希望能单独拥有它,
            那么就需要使用线程存储了。表面上看起来这是一个全局变量,所有线程都可以使用它,
            而它的值在每一个线程中又是单独存储的。这就是线程存储的意义。

            当然需要 “ 线程键”的参与!!!
           
            intpthread_setspecific(pthread_key_t key, const void*value);      //!> 存储特殊值(即自己单独需要的值)
            void*pthread_getspecific(pthread_key_tkey);                              //!> 取出所存储的值
           

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值