本文主要想跟大家分享一下自己在多线程学习过程中遇见的一些问题,以及关于该部分内容的知识整理和总结。
1.从内核来看,早期的Linux系统并没有线程的概念,Linux里面的线程就是一个轻量级的进程(需要注意的是:这只是针对Linux系统而言,但是对于其他的操作系统却并没有这种说法)。
2.进程VS线程
(1)进程是资源分配的最小单位,线程是操作系统调度执行的最小单位。
(2)每创建一个进程系统就会为该进程分配一个独立的地址空间;但是对于线程而言,多个线程共用一个地址空间,所以相比于进程线程更加的节约系统资源。
(3)线程的上下文切换要比进程快的多。
上下文切换:进程/线程分时复用cpu时间片,在切换之前会将上一个任务的状态进行保存,下次切换这个任务的时候,会加载这个状态继续执行,任务从保存到再次加载这个过程就是一次上下文切换。
(4)相比于进程,线程确实有很多的优势,但是线程并不是越多越好。一般情况下:如果只是处理IO操作,那么线程数最多为cpu内核数的两倍;如果处理的是复杂的算法操作,那么线程数应该与CPU内核数相同。(按照以上的情况,处理效率比较高)
1.线程创建函数
函数原型:
函数原型:
int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(start_rountin)(void *) , void *args);
(1)pthread_t *thread:该参数是一个传出参数(pthread_t其实就是无符号长整数),只要线程创建成功,会将线程ID写入到thread指针指向的内存中。
(2)const pthread_attr_t *attr:该参数表示的是线程的属性,一般情况下该参数填写默认值(NULL)就可以了。
(3)void *(start_rountin)(void *):该参数是一个函数指针(一般情况下,函数指针作为参数一般都是做回调用的),函数指针指向线程中要执行的任务的回调函数,具体是什么任务跟该线程的回调函数有关。
(4) void *args:void *其实就是一个泛型,说明我们可以把任意类型的参数传递给回调函数;但如果要传多种类型的数据,只需要定义一个结构体类型的数据,并将结构体的地址进行传入即可实现。
值得注意的是:进程在调用线程创建函数的之后,该进程会自动转换为主线程。
#include
#include
#include
#include
void* callback(void* args)
{
printf("son_thread :%ld\n",pthread_self());
return NULL;
}
int main()
{
pthread_t thr1;
pthread_create(&thr1,NULL,callback,NULL);
printf("---------------------\n");
printf("main_thread:%ld\n",pthread_self());
return 0;
}
编译运行:
gongyuan@ubuntu:~$ vim hello.c
gongyuan@ubuntu:~$ gcc hello.c -lpthread -o p
gongyuan@ubuntu:~$ ./p
---------------------
main_thread:140508581807936
问题1:为什么子进程回调函数里面的内容没有显示出来?
解答:因为子线程还没有抢到时间片,主线程就结束了,就把地址空间释放了,所以子线程也跟着随之结束。
解决办法:
可以在主线程return语句之前,加上sleep函数,使主线程休眠一会,等子线程执行完成之后再退出;
也可以使用线程退出函数。
int main()
{
pthread_t thr1;
pthread_create(&thr1,NULL,callback,NULL);
printf("---------------------\n");
printf("main_thread:%ld\n",pthread_self());
sleep(3);
return 0;
}
编译运行:
gongyuan@ubuntu:~$ vim hello.c
gongyuan@ubuntu:~$ gcc hello.c -lpthread -o p
gongyuan@ubuntu:~$ ./p
---------------------
main_thread:140189573818176
son_thread :140189573814016
2.线程退出函数
函数原型:
void pthread_exit(void *retval)(里面的参数默认情况下写NULL)
线程退出函数的作用
线程退出函数可以某一个线程退出之后,不影响其他线程的运行。该函数主要使用进行主线程的退出,因为对于子线程而言,是否使用线程退出函数并不会影响到其他的子线程的运行,该函数会结合线程回收函数来一起进行使用。
3.线程回收函数
函数原型:
int pthread_join(pthread_t thread, void **retval)
pthread_t thread:线程的ID,用于指定需要进行回收的线程。
void **retval:二级指针,指向一级指针的地址。
作用:线程回收函数是主线程用来回收子线程的资源的,这部分资源存储在子线程的内核区。
注意:调用pthread_join()函数的时候会一直阻塞等待子线程退出,如果子线程不退出,join就会一直阻塞等待,而且每调用一次pthread_join()函数,它只回收一个子线程,如果要回收多个子线程资源需要循环的进行该函数的调用。
#include
#include
#include
#include
//定义结构体变量
struct Test
{
int num;
int age;
};
//线程1的回调函数
void* callback(void* arg)
{
printf("SS:I emm emm just conscientious in study\n");
struct Test* t = (struct Test*)arg;
t->num = 1000;
t->age = 6;
pthread_exit(t);
return NULL;
}
//线程2的回调函数
void* callback1(void* arg)
{
printf("ME:Are you kidding me,I konw what you are doing; hehe\n");
}
int main()
{
struct Test t;
pthread_t thr1;
pthread_t thr2;
printf("ME:what are you doing SS\n");
pthread_create(&thr1,NULL,callback,&t);
void* ptr;
//这里的ptr指针是为了对接收到的t进行初始化操作
pthread_join(thr1,&ptr);
struct Test* pt = (struct Test*)ptr;
printf("num:%d.age:%d\n",pt->num,pt->age);
pthread_create(&thr1,NULL,callback1,NULL);
sleep(3);
return 0;
}
运行结果:
gongyuan@ubuntu:~$ vim SS_day.c +16
gongyuan@ubuntu:~$ gcc SS_day.c -lpthread -o p
gongyuan@ubuntu:~$ ./p
ME:what are you doing SS
SS:I emm emm just conscientious in study
num:1000.age:6
ME:Are you kidding me,I konw what you are doing; hehe
gongyuan@ubuntu:~$
第二次发博客,希望大家可以多多支持和关注,加油!!!