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('\n');
    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

Linux多线程实践(2) --线程基本API的更多相关文章

  1. Linux多线程实践(1) --线程理论

    线程概念 在一个程序里的一个执行路线就叫做线程(thread).更准确的定义是:线程是"一个进程内部的控制序列/指令序列"; 一切进程至少有一个执行线程; 进程  VS. 线程  ...

  2. Linux多线程实践(4) --线程特定数据

    线程特定数据 int pthread_key_create(pthread_key_t *key, void (*destr_function) (void *)); int pthread_key_ ...

  3. Linux多线程实践(3) --线程属性

    初始化/销毁线程属性 int pthread_attr_init(pthread_attr_t *attr); int pthread_attr_destroy(pthread_attr_t *att ...

  4. Linux多线程实践(三)线程的基本属性设置API

    POSIX 线程库定义了线程属性对象 pthread_attr_t ,它封装了线程的创建者能够訪问和改动的线程属性.主要包含例如以下属性: 1. 作用域(scope) 2. 栈尺寸(stack siz ...

  5. Linux多线程实践(9) --简单线程池的设计与实现

    线程池的技术背景 在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源.在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收.所以 ...

  6. Linux多线程实践(一)线程基本概念和理论

    线程概念 在一个程序里的一个运行路线就叫做线程(thread).更准确的定义是:线程是"一个进程内部的控制序列/指令序列"; 对于每一个进程至少有一个运行线程; 进程  VS. 线 ...

  7. Linux多线程实践(四 )线程的特定数据

    在单线程程序中.我们常常要用到"全局变量"以实现多个函数间共享数据, 然而在多线程环境下.因为数据空间是共享的.因此全局变量也为全部线程所共同拥有.但有时应用程序设计中有必要提供线 ...

  8. Linux多线程实践(10) --使用 C++11 编写 Linux 多线程程序

    在这个多核时代,如何充分利用每个 CPU 内核是一个绕不开的话题,从需要为成千上万的用户同时提供服务的服务端应用程序,到需要同时打开十几个页面,每个页面都有几十上百个链接的 web 浏览器应用程序,从 ...

  9. Linux多线程实践(8) --Posix条件变量解决生产者消费者问题

    Posix条件变量 int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr); int pthread_co ...

随机推荐

  1. Java 程序运行过程中的内存分析

    作为 java 程序员,都应该知道 Java 程序运行在 JVM(Java Virtual Machine,Java 虚拟机)上,可以把 JVM 理解成 Java 程序和操作系统之间的桥梁,JVM 实 ...

  2. PHP 常用函数集合

    PHP is_numeric() 函数 由 陈 创建, 最后一次修改 2016-12-02 定义和用法 is_numeric() - 检测变量是否为数字或数字字符串 语法 bool is_numeri ...

  3. 判定程序员等级,HashMap就够了

    JDK1.8  HashMap源码分析 用到的符号: ^异运算:两个操作数相同,结果是;两个操作数不同,结果是1. &按位与:两个操作数都是1,结果才是1. 一.HashMap概述 在JDK1 ...

  4. Java 零散笔记

    运算符: 整数被0除将会产生一个异常,而浮点数被0除会得到无穷大或NaN结果. 二元运算符: 如果运算符得到一个值,其类型与左侧操作数的类型不用,就会发生强制类型转换. int x = 0; x += ...

  5. Java第7次实验提纲(多线程)

    PTA与参考资料 题集:多线程 多线程实验参考文件 ThreadReading 实验-基础部分 1.1 基础题目MyThread类.自行完成题集合的:PrintTask 1.2 Runnable与匿名 ...

  6. Python 3.3.2 round函数并非"四舍五入"

    对于一些貌似很简单常见的函数,最好还是去读一下Python文档,否则当你被某个BUG折磨得死去活来时,还不知根源所在.尤其是Python这种不断更新的语言.(python 2.7 的round和3.3 ...

  7. 20160212.CCPP体系详解(0022天)

    程序片段(01):01.二维数组.c 内容概要:二维数组 #include <stdio.h> #include <stdlib.h> //01.关于栈内存开辟数组: // 诀 ...

  8. 干货:MySQL 索引原理及慢查询优化

    MySQL凭借着出色的性能.低廉的成本.丰富的资源,已经成为绝大多数互联网公司的首选关系型数据库.虽然性能出色,但所谓"好马配好鞍",如何能够更好的使用它,已经成为开发工程师的必修 ...

  9. Markdown-----Markdown使用文档

    最近才接触Markdown,为了快速记忆,整理了这个文档,欢迎补充. Markdown和扩展Markdown简洁的语法 代码块高亮 图片链接和图片上传 LaTex数学公式 UML序列图和流程图 离线写 ...

  10. everything of people’s life can changed in their twenties

    还记得三年前,独自背着行李,流浪远方,来到曾经只在地理课本上才熟悉的北国,带着好奇,带着期望,带着激动的心情,想感受毛爷爷当年霸气的北国风光,千里冰封的美丽,想知道北方的面条到底有多少种花样,想走进那 ...