Linux多线程(上)
1.线程概念及优缺点
- 线程是一个轻量级进程,准确的定义是:一个进程内部的控制序列
- 一切进程至少都有一个执行进程
- 线程在进程内部运行,本质是在进程的地址空间内运行
- 通过进程地址空间,将进程资源合理分配给每个执行流,就形成了线程执行流
共享与独有:
- 独有:在进程虚拟地址空间的共享区当中,调用栈,寄存器,线程ID,errno,信号屏蔽字,调度优先级
- 共享:文件描述符表,用户id,用户组id, 信号处理方式(SIG_IGN、SIG_DFL或者自定义的信号处理函数),当前进程的工作目录
优点:
- 创建一个新线程的代价要比创建一个新进程小得多
- 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
- 线程占用的资源要比进程少很多
- 能充分利用多处理器的可并行数量
- 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
- I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。
缺点:
- 编写代码的难度更高了
- 代码的鲁棒性(稳定性)要求更高了
- 线程数量并不是越多越好
- 缺乏访问控制,可能会导致程序产生二义性的结果
- 一个线程崩溃,会导致整个进程退出
2.线程创建
接口:
- int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(start_routine)(void), void *arg);
- 参数:
thread:返回线程id attr:设置线程的属性,attr为空表示默认属性 start_routine:是个函数地址,线程启动后要执行的函数 arg:传送给线程启动函数的参数 返回值:成功返回0,失败返回错误码
代码:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
struct Data_i{
int i;
};
void* my_thread_start(void* arg){
struct Data_i* p = (struct Data_i*)arg;
while(1){
printf("i am work thread!: %d\n", p->i);
sleep(1);
}
delete p;
}
int main()
{
for(int i = 0; i < 5; i++){
struct Data_i* lp = new Data_i();
lp->i = i;
pthread_t tid;
int ret = pthread_create(&tid, NULL, my_thread_start, (void*)lp);
if(ret < 0){
perror("pthread_create");
return 0;
}
}
while(1){
printf("i am main thread\n");
sleep(1);
}
return 0;
}
3.线程终止
接口:
- void pthread_exit(void *retval);
- retval:线程退出时,传递给等待线程的退出信息(不要指向一个局部变量,必须是全局的或者是malloc分配的内存单元,不能在线程函数的栈上分配,因为当其他线程得到这个返回指针时线程函数已经退出了)
- 返回值:无返回值,跟进程一样,线程结束时无法返回它的调用者(自身)
- 作用:谁调用谁退出
- int pthread_cancel(pthread_t thread);
- thread:线程id(被终止的线程标识符)
- 返回值:成功返回0,失败返回错误码
- 作用:取消一个执行中的线程(谁调用获取谁的线程标识符)
代码实现:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
void* my_thread_start(void* arg){
while(1){
printf("i am work thread\n");
sleep(1);
}
}
int main(){
pthread_t tid;
int ret = pthread_create(&tid, NULL, my_thread_start, NULL);
if(ret < 0){
perror("pthread_create");
return 0;
}
getchar();
pthread_cancel(pthread_self());
return 0;
}
4.线程等待
为什么要线程等待?
线程被创建出来的默认属性是joinable属性,退出的时候,其空间没有被释放,仍然在进程的地址空间内,依赖其他进程来回收资源,并且创建新的线程不会复用刚才退出的线程的地址空间(主要是退出线程使用到的是共享区当中的空间)
接口:
- int pthread_join(pthread_t thread, void **retval);
- 参数:
thread:线程的标识符 retval:退出线程的退出信息 - 种类:
1.线程入口函数代码执行完毕,线程退出的就是入口函数的返回值 2.pthread_exit退出的,就是pthead_exit的参数 3.pthead_cancel退出的就是一个宏:PTHREAD_CANCELED - 实际上是一个阻塞调用窗口
代码实现:
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
void* my_thread_start(void *arg){
(void)arg;
int count=5;
while(count--){
printf("i am work thread\n");
sleep(1);
}
return NULL;
}
int main(){
pthread_t tid;
int ret=pthread_create(&tid,NULL,my_thread_start,NULL);
if(ret<0){
perror("pthread_create");
return 0;
}
pthread_join(tid,NULL);
return 0;
}
5.线程分离
为什么要进行线程分离?
- 默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法释
放资源,从而造成系统泄漏。 - 因此设置线程的分离属性,一旦线程设置了分离属性,则线程退出的时候,不需要任何人回收资源。操作系统可以进行回收
接口:
- int pthread_detach(pthread_t thread);
thread:设置线程分离的线程的标识符
代码实现:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
void* my_thread_start(void* arg){
pthread_detach(pthread_self());
(void)arg;
int count = 10;
while(count--){
printf("i am work thread\n");
sleep(1);
}
return NULL;
}
int main(){
pthread_t tid;
int ret = pthread_create(&tid, NULL, my_thread_start, NULL);
if(ret < 0){
perror("pthread_create");
return 0;
}
while(1){
sleep(1);
}
return 0;
}
|