zoukankan      html  css  js  c++  java
  • Linux多线程实践(2) --线程基本API

    POSIX线程库

      与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”开头,要使用这些函数库,要通过引入头文<pthread.h>,而且链接这些线程函数库时要使用编译器命令的“-lpthread”选项[Ubuntu系列系统需要添加的是”-pthread”选项而不是”-lpthread”,如Ubuntu 14.04版本,深度Ubuntu等]

     

    1.pthread_create

    int pthread_create(pthread_t *restrict thread,
    		const pthread_attr_t *restrict attr,
    		void *(*start_routine)(void*), void *restrict arg);

    创建一个新的线程

    参数

      thread:线程ID

      attr:设置线程的属性,一般设置为NULL表示使用默认属性

      start_routine:是个函数地址,线程启动后要执行的函数

      arg:传给线程启动函数的参数

    返回值:成功返回0;失败返回错误码;

     

    附-Posix错误检查

      UNIX传统的函数:成功返回0,失败返回-1,并且对设置全局变量errno以指定错误类型。然而pthreads函数出错时不会设置全局变量errno(而其他的大部分POSIX函数会设置errno)。而是将错误代码通过返回值返回;

      pthreads同样也提供了线程内的errno变量,对于每一个线程, 都有一个errno的值, 以支持其它使用errno的代码。对于pthreads函数的错误,建议通过返回值进行判定,因为读取返回值要比读取线程内的errno变量的开销更小!

    /** 实践: 新的错误检查与错误退出函数 **/
    inline void err_check(const std::string &msg, int retno)
    {
        if (retno != 0)
            err_exit(msg, retno);
    }
    inline void err_exit(const std::string &msg, int retno)
    {
        std::cerr << msg << ": " << strerror(retno) << endl;
        exit(EXIT_FAILURE);
    }

    2.pthread_exit

    void pthread_exit(void *value_ptr);

    线程终止

      value_ptr:指向该线程的返回值;注意:value_ptr不能指向一个局部变量

     

    3.pthread_join

    int pthread_join(pthread_t thread, void **value_ptr);

    等待线程结束

      value_ptr:它指向一个指针,后者指向线程的返回值(用户获取线程的返回值)

    /** 示例: 等待线程退出 **/
    void *thread_rotine(void *args)
    {
        for (int i = 0; i < 10; ++i)
        {
            printf("B");
            fflush(stdout);
            usleep(20);
        }
        pthread_exit(NULL);
    }
    
    int main()
    {
        pthread_t thread;
        int ret = pthread_create(&thread, NULL, thread_rotine, NULL);
        err_check("pthread_create", ret);
    
    
        for (int i = 0; i < 10; ++i)
        {
            printf("A");
            fflush(stdout);
            usleep(20);
        }
    
        ret = pthread_join(thread, NULL);
        err_check("pthread_join", ret);
        putchar('
    ');
        return 0;
    }

    4.pthread_self

    pthread_t pthread_self(void);

    返回线程ID

    /** 示例:主控线程与子线程传递数据 **/
    typedef struct _Student
    {
        char name[20];
        unsigned int age;
    } Student;
    
    void *threadFunction(void *args)
    {
        cout << "In Thread: " << pthread_self() << endl;
        Student tmp = *(Student *)(args);
        cout << "Name: " << tmp.name << endl;
        cout << "Age: " << tmp.age << endl;
    
        pthread_exit(NULL);
    }
    
    int main()
    {
        Student student = {"xiaofang",22};
    
        pthread_t thread;
        //启动创建并启动线程
        pthread_create(&thread,NULL,threadFunction,&student);
        //等待线程结束
        pthread_join(thread,NULL);
    
        return 0;
    }


    5.pthread_cancel

    int pthread_cancel(pthread_t thread);

    取消一个执行中的线程


    6.pthread_detach

    int pthread_detach(pthread_t thread);

      将一个线程分离-如果在新创建的线程结束时主线程没有结束同时也没有调用pthread_join,则会产生僵线程,次问题可以通过设置线程为分离的(detach)来解决;

     

    总结:进程 VS. 线程

    进程(pid_t)

    线程(pthread_t)

    Fork

    Pthread_create

    Waitpit

    Pthread_join/Pthread_detach

    Kill

    Pthread_cancel

    Pid

    Pthead_self

    Exit/return

    Pthread_exit/return

    僵尸进程(没有调用wait/waitpid等函数)

    僵尸线程(没有调用pthread_join/pthread_detach)

    /** 将并发echo server改造成多线程形式 
    注意线程竞速问题的解决
    **/
    void echo_server(int clientSocket);
    void *thread_routine(void *arg);
    int main()
    {
        int sockfd = socket(AF_INET,SOCK_STREAM,0);
        if (sockfd == -1)
            err_exit("socket error");
    
        int optval = 1;
        if (setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval)) == -1)
            err_exit("setsockopt error");
    
        struct sockaddr_in serverAddr;
        serverAddr.sin_family = AF_INET;
        serverAddr.sin_port = htons(8002);
        serverAddr.sin_addr.s_addr = INADDR_ANY;    //绑定本机的任意一个IP地址
        if (bind(sockfd,(struct sockaddr *)&serverAddr,sizeof(serverAddr)) == -1)
            err_exit("bind error");
    
        if (listen(sockfd,SOMAXCONN) == -1)
            err_exit("listen error");
    
        while (true)
        {
            int peerSockfd = accept(sockfd, NULL, NULL);
            if (peerSockfd == -1)
                err_exit("accept error");
    
            pthread_t tid;
            /**注意: 下面这种用法可能会产生"竞速问题"
                    当另一个连接快读快速到达, peerSockfd的内容更改,
                    新创建的线程尚未将该值取走时,线程读取的就不是
                    我们想让线程读取的值了
            int ret = pthread_create(&tid, NULL, thread_routine, (void *)&peerSockfd);
            **/
            //解决方案: 为每一个链接创建一块内存
            int *p = new int(peerSockfd);
            int ret = pthread_create(&tid, NULL, thread_routine, p);
            if (ret != 0)
                err_thread("pthread_create error", ret);
        }
        close(sockfd);
    }
    void *thread_routine(void *args)
    {
        //将线程设置分离状态, 避免出现僵尸线程
        pthread_detach(pthread_self());
        int peerSockfd = *(int *)args;
        //将值取到之后就将这块内存释放掉
        delete (int *)args;
    
        echo_server(peerSockfd);
        cout << "thread " << pthread_self() << " exiting ..." << endl;
        pthread_exit(NULL);
    }
    void echo_server(int clientSocket)
    {
        char buf[BUFSIZ] = {0};
        int readBytes;
        while ((readBytes = read(clientSocket, buf, sizeof(buf))) >= 0)
        {
            if (readBytes == 0)
            {
                cerr << "client connect closed" << endl;
                break;
            }
            if (write(clientSocket, buf, readBytes) == -1)
            {
                cerr << "server thread write error" << endl;
                break;
            }
            cout << buf;
            bzero(buf, sizeof(buf));
        }
    }

    其完整源代码:download.csdn.net/detail/hanqing280441589/8440763


  • 相关阅读:
    RabbitMQ 内存控制 硬盘控制
    Flannel和Docker网络不通定位问题
    kafka集群扩容后的topic分区迁移
    CLOSE_WAIT状态的原因与解决方法
    搭建Harbor企业级docker仓库
    Redis哨兵模式主从持久化问题解决
    mysql杂谈(爬坑,解惑,总结....)
    Linux的信号量(semaphore)与互斥(mutex)
    SIP协议的传输层原理&报文解析(解读rfc3581)(待排版) && opensips
    SIP协议的传输层原理&报文解析(解读RFC3261)(待排版)&&启动
  • 原文地址:https://www.cnblogs.com/itrena/p/5926952.html
Copyright © 2011-2022 走看看