zoukankan      html  css  js  c++  java
  • APUE学习之多线程编程(一):线程的创建和销毁

    一、线程标识
         和每个进程都有一个进程ID一样,每个线程也有一个线程ID,线程ID是以pthread_t数据类型来表示的,在Linux中,用无符号长整型表示pthread_t,Solaris 把phread_t数据类型表示为无符号整型,FreeBSD 和Mac OS X 用一个指向pthread结构的指针来表示pthread_t数据类型。
         可以使用pthread_self函数获得自身的线程ID。   
    #include <pthread.h>
     pthread_t pthread_self(void);
     
    二、线程创建
         使用pthread_create函数创建新线程 
    #include <pthread.h>
    int pthread_create(pthread_t *restrict tidp, const pthread_arrt_t *restrict addr, void *(*start_rtn)(void *), void *restrict arg);
         当pthread_create成功返回后,新创建线程的线程ID会被设置成tidp指向的内存单元,attr参数用于定制各种不同的线程属性,后面再讨论线程属性,现在先把它置为null,创建一个具有默认属性的线程。
         新创建的线程从start_rtn函数开始运行,该函数接收一个无类型指针的参数arg,如果要传给它的参数多于一个,可以把参数放到一个结构中,然后把结构的地址作为arg传入。
         线程新建后会继承调用线程的浮点环境和屏蔽字。
    例子:  
    #include "apue.h"
    #include <pthread.h>
    
    pthread_t ntid;
    
    void printids(const char *s)
    {
        pid_t pid;
        pthread_t tid;
    
        pid = getpid();
        tid = pthread_self();
        printf("%s pid %lu tid %lu (0x%lx)
    ", s, (unsigned long)pid, (unsigned long)tid, (unsigned long)tid);
    }
    
    void *thr_fn(void *arg)
    {
        printids("new thread: ");
        return ((void *)0);
    }
    
    int main(void)
    {
        int err;
    
        err = pthread_create(&ntid, NULL, thr_fn, NULL);
        if (err != 0)
        {
            err_exit(err, "can't create thread");
        }
    
        printids("main thread: ");
        sleep(1);
        exit(0);
    }
    View Code
      这个程序有两个特别的地方:第一,主线程需要休眠,如果主线程不休眠,主线程会退出,新线程并没有机会运行。第二,新线程通过pthread_self(),获得自己的线程ID。
    ./a.out
    main thread:  pid 27335 tid 3076404928 (0xb75e36c0)
    new thread:  pid 27335 tid 3076401984 (0xb75e2b40)
    View Code
      虽然Linux线程ID是用无符号长整型来表示的,但它们看起来更像指针。
     
    三、线程终止
         如果任意线程调用了exit,_exit,_Exit,整个进程都会终止,这个要注意。
         单个线程可以通过以下三种方式退出,且不终止整个进程。
         1.线程可以简单地从启动例程中返回,返回值是线程的退出码。
         2.线程可以被同一进程中的其他线程取消。
         3.调用pthread_exit
     
         先来看pthread_exit退出的情况。
    #include <pthread.h>
    void pthread_exit(void *rval_ptr);

         ravl_ptr是无类型指针,进程中的其他线程可以通过pthread_join函数获得这个指针。

    #include <pthread.h>
    int pthread_join(pthread_t thread, void **rval_ptr);

         调用线程将一直阻塞,直至指定的线程退出,rval_ptr就包含返回码,如果线程被取消,rval_ptr指定的内存单元就设置为PTHREAD_CANCELED.可以通过调用pthread_join自动把线程置于分离状态,如果线程已处于分离状态,pthread_join就会调用失败。

    例子:
    #include "apue.h"
    #include <pthread.h>
     
    void *thr_fn1(void *arg)
    {
        printf("thread 1 returning
    ");
        return (void *)1;
    }
     
    void *thr_fn2(void *arg)
    {
        printf("thread 2 exiting
    ");
        pthread_exit((void *)2);
    }
     
    int main(void)
    {
        int err;
        pthread_t tid1, tid2;
        void *tret;
     
        err = pthread_create(&tid1, NULL, thr_fn1, NULL);
     
        if (err != 0)
        {
            err_exit(err, "can't create thread1");
        }
     
        err = pthread_create(&tid2, NULL, thr_fn2, NULL);
     
        if (err != 0)
        {
            err_exit(err, "can't create thread2");
        }
     
        err = pthread_join(tid1, &tret);
     
        if (err != 0)
        {
            err_exit(err, "can't join thread1");
        }
     
        printf("thread1 exit code:%ld
    ", (long)tret);
     
        err = pthread_join(tid2, &tret);
     
        if (err != 0)
        {
            err_exit(err, "can't join thread2");
        }
     
        printf("thread2 exit code:%ld
    ", (long)tret);
     
        return 0;
    }
    View Code
    ./a.out
    thread 2 exiting
    thread 1 returning
    thread1 exit code:1
    thread2 exit code:2
    View Code
      也可传递包含复杂消息的结构的地址,不过必须注意,这个结构所使用的内存必须在完成调用后仍是有效的。
          线程也可以调用pthread_cancel函数来请求取消同一进程的其他线程
    #include <pthread.h>
    int pthread_cancel(pthread_t tid);

         听着有点霸道,不过也只是请求而已,线程可以选择忽略这个请求。

         线程可以安排它退出时需要调用的函数,这样的函数是由pthread_cleanup_push注册在栈中的,所以执行顺序与注册时相反。
    #include <pthread.h>
    void pthread_cleanup_push(void(*rtn)(void *), void *arg);
    void pthread_cleanup_pop(int execute);

         当线程执行以下动作时,清理函数rtn由pthread_cleanup_push函数调度

         1.调用pthread_exit时
         2.响应取消请求时
         3.用非零execute参数调用pthread_cleanup_pop时。
    例子:
    #include "apue.h"
    #include <pthread.h>
     
    void cleanup(void *arg)
    {
        printf("cleanup: %s
    ", (char *)arg);
    }
     
    void *thr_fn1(void *arg)
    {
        printf("thread 1 start
    ");
        pthread_cleanup_push(cleanup, "thread 1 first handler");
        pthread_cleanup_push(cleanup, "thread 1 second handler");
        printf("thread 1 push complete
    ");
     
        if (arg)
        {
            return (void *)1;
        }
     
        pthread_cleanup_pop(0);
        pthread_cleanup_pop(0);
     
        return (void *)1;
    }
     
    void *thr_fn2(void *arg)
    {
        printf("thread 2 start
    ");
        pthread_cleanup_push(cleanup, "thread 2 first handler");
        pthread_cleanup_push(cleanup, "thread 2 second handler");
        printf("thread 2 push complete
    ");
     
        if (arg)
        {
            pthread_exit((void *)2);
        }
     
        pthread_cleanup_pop(0);
        pthread_cleanup_pop(0);
     
        return (void *)2;
    }
     
    int main(void)
    {
        int err;
        pthread_t tid1, tid2;
        void *tret;
     
        err = pthread_create(&tid1, NULL, thr_fn1, (void *)1);
        if (err != 0)
        {
            err_exit(err, "can't create thread 1");
        }
     
        err = pthread_create(&tid2, NULL, thr_fn2, (void *)1);
        if (err != 0)
        {
            err_exit(err, "can't create thread 2");
        }
     
        err = pthread_join(tid1, &tret);
        if (err != 0)
        {
            err_exit(err, "can't join with thread 1");
        }
        printf("thread 1 exit code %ld
    ", (long)tret);
     
        err = pthread_join(tid2, &tret);
        if (err != 0)
        {
            err_exit(err, "can't join with thread 2");
        }
        printf("thread 2 exit code %ld
    ", (long)tret);
     
        return 0;
    }
    View Code 
    ./a.out
    thread 2 start
    thread 2 push complete
    cleanup: thread 2 second handler
    cleanup: thread 2 first handler
    thread 1 start
    thread 1 push complete
    thread 1 exit code 1
    thread 2 exit code 2
    View Code
         可知如果线程是通过它的启动例程中返回而终止的话,它的清理处理程序就不会被调用。
     
         在默认情况下,线程的终止状态会一直保存到对该线程调用pthread_join,如果该线程已经被分离,则底层的资源可以在线程终止时立即被收回,不用再调用pthread_join,且再调用pthread_join会出错。
    #include <pthread.h>
    int pthread_detach(pthread_t tid);
  • 相关阅读:
    事务使用测试结论
    Enum枚举2
    Enum枚举
    sqlserver数据库存取图片
    sqlserver数据库操作公共类DBOperate
    SqlParamsTool
    sqlserver数据库 -- SqlParameter使用
    sqlserver 数据库连接状态判断
    水晶报表2
    防止系统页面被加载进 iframe 子窗口
  • 原文地址:https://www.cnblogs.com/shenlinken/p/5775001.html
Copyright © 2011-2022 走看看