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('\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的更多相关文章
- Linux多线程实践(1) --线程理论
线程概念 在一个程序里的一个执行路线就叫做线程(thread).更准确的定义是:线程是"一个进程内部的控制序列/指令序列"; 一切进程至少有一个执行线程; 进程 VS. 线程 ...
- Linux多线程实践(4) --线程特定数据
线程特定数据 int pthread_key_create(pthread_key_t *key, void (*destr_function) (void *)); int pthread_key_ ...
- Linux多线程实践(3) --线程属性
初始化/销毁线程属性 int pthread_attr_init(pthread_attr_t *attr); int pthread_attr_destroy(pthread_attr_t *att ...
- Linux多线程实践(三)线程的基本属性设置API
POSIX 线程库定义了线程属性对象 pthread_attr_t ,它封装了线程的创建者能够訪问和改动的线程属性.主要包含例如以下属性: 1. 作用域(scope) 2. 栈尺寸(stack siz ...
- Linux多线程实践(9) --简单线程池的设计与实现
线程池的技术背景 在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源.在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收.所以 ...
- Linux多线程实践(一)线程基本概念和理论
线程概念 在一个程序里的一个运行路线就叫做线程(thread).更准确的定义是:线程是"一个进程内部的控制序列/指令序列"; 对于每一个进程至少有一个运行线程; 进程 VS. 线 ...
- Linux多线程实践(四 )线程的特定数据
在单线程程序中.我们常常要用到"全局变量"以实现多个函数间共享数据, 然而在多线程环境下.因为数据空间是共享的.因此全局变量也为全部线程所共同拥有.但有时应用程序设计中有必要提供线 ...
- Linux多线程实践(10) --使用 C++11 编写 Linux 多线程程序
在这个多核时代,如何充分利用每个 CPU 内核是一个绕不开的话题,从需要为成千上万的用户同时提供服务的服务端应用程序,到需要同时打开十几个页面,每个页面都有几十上百个链接的 web 浏览器应用程序,从 ...
- Linux多线程实践(8) --Posix条件变量解决生产者消费者问题
Posix条件变量 int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr); int pthread_co ...
随机推荐
- Git-gitblit-Tortoisegit 搭建Windows Git本地服务器
1.Gitblit安装 1.1.Gitblit简介 Git在版本控制领域可谓是深受程序员喜爱.对于开源的项目,可以免费托管到GitHub上面,相当的方便.但是私有项目托管到GitHub会收取相当昂贵的 ...
- JAVA中抽象类的使用
抽象类是从多个具体类中抽象出来的父类,它具有更高层次的抽象.抽象类体现的就是一种模板模式的设计,抽象父类可以只定义需要使用的某些方法,把不能实现的某些部分抽象成抽象方法,留给其子类去实现.具体来说,抽 ...
- Node.js 虚拟机
稳定性: 3 - 稳定 可以通过以下方法访问该模块: var vm = require('vm'); JavaScript 可以立即编译立即执行,也可以编译,保存,之后再运行. vm.runInThi ...
- Angular2学习笔记2
每个angular2应用程序默认使用app目录来创建(可以自己制定,但是eclipse插件生成的会自动使用app) 每个程序应当至少有一个angular模块即根模块.根模块使用@NgModule({} ...
- Docker简介/安装/使用
什么是Docker?docker是一个开源的应用容器引擎,系统级的轻量虚拟化技术.应用程序的自动化部署解决方案,能够迅速创建一个容器,并在容器上部署和运行应用程序,并通过配置文件可以轻松实现应用程序的 ...
- android launcher 之踩到的坑
需求: 1. 用android系统launcher 隐藏主菜单 所有应用显示在桌面 即workspace上: 2.隐藏launcher上方默认的google search: 3.切换一套launche ...
- Android新特性Instant Run详解
关于 Instant Run Android Studio 2.0 中引入的 Instant Run 是 Run 和 Debug 命令的行为,可以大幅缩短应用更新的时间.尽管首次构建可能需要花费较长的 ...
- JAVA面向对象-----局部内部类
局部内部类 局部内部类概述:包含在外部类的函数中的内部类称之为局部内部类. 访问:可以在包含局部内部类的方法中直接创建局部内部类的对象调用局部内部类的成员. 注意:局部内部类只能访问所在函数的fana ...
- EBS业务学习之应付INVOICE类型
INVOICE类型 类 型 描 述 标准INVOICE 是指由于采购货物或接受劳务,从供应商处取得的INVOICE (标准INVOICE,既可以和订单匹配,也可以不匹配) ...
- 如何将dtb反编译成dts
点击打开链接 由于device tree会将一个node的信息分布在各个文件里,查看起来很不方便,比如如下例子,ldb在三个文件中都有配置: imx6qdl-sabresd.dtsi: [plain] ...