1.线程概述
程序运行起来编程进程,进程由一个个线程构成。
eg:
没有启动的qq时一个程序,启动后登录qq,qq是一个进程,实际上进程什么都没做,只是提供了需要的资源,打开聊天框可以和别人进行通信,这就是线程,和多个人聊天就是多线程。
每个进程中至少包含一个主线程。
线程是轻量级的进程(LWP:light weight process),再linux环境下的本质仍然是基础。
进程是最小的资源分配单位,线程是最小的执行单位。
2.线程资源
线程只共享:
- 文件描述符
- 每种信号的处理方式
- 当前工作目录
- 用户ID和组ID,内存地址空间
独占的资源:(栈区不共享)
- 线程ID
- 处理器现场和栈指针(内核栈)
- 独立的栈空间(用户空间栈)
- errno变量
- 信号屏蔽字
- 调度优先级
3.线程的优点和缺点
优点:
- 提高程序并发性
- 开销小
- 数据通信、共享数据方便
缺点:
- 库函数,不稳定
- 调试、编写困难、gdb不支持
- 对信号支持不好
4.线程的创建
4.1 线程号
线程号在对应的进程里是唯一的。
进程号:pid_t 非负整数
线程号:pthread_t 无符号长整型
4.2 API
4.2.1 pthread_self()
#include <pthread.h>
pthread_t pthread_self(void);
函数功能:
- 获取当前线程的线程号
返回值:
- 获取的线程号
后续如果遇到编译不通过,提示pthread相关,可以编译的时候引入相关资源。 -pthread
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>int main(int argc, char const *argv[])
{pid_t pid=getpid();//获取进程号pthread_t pt1=pthread_self();//获取当前进程的主线程号printf("pid:%u,%lu\n",pid,pt1); return 0;
}
4.2.2 pthread_create()
#include <pthread.h>
int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start_routine)(void *),void *arg );
函数功能:
- 创建一个线程
参数:
- thread:线程标识符地址。
- attr:线程属性结构体地址,通常设置为 NULL。
- start_routine:线程函数的入口地址。函数指针。
- arg:传给线程函数的参数。
返回值:
- 成功:0
- 失败:非 0
线程函数不传参:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>void *func(void *argv)
{printf("线程%lu正在运行\n", pthread_self());
}int main(int argc, char const *argv[])
{pid_t pid = getpid(); // 获取进程号pthread_t pt1 = pthread_self(); // 获取当前进程的主线程号printf("进程:%u,主线程:%lu\n", pid, pt1);pthread_t pt2;int ret = pthread_create(&pt2, NULL, func, NULL); // 创建线程if (ret != 0){perror("pthread_create");return 0;}// 在此处阻塞,避免线程未执行,进程就结束,进程结束,系统会回收进程资源// 线程之间是并发的,而不是简单的从上往下执行sleep(1);return 0;
}
线程函数传参:数组
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>void *func(void *argv)
{printf("线程:%lu 内容:%s\n", pthread_self(), (char *)argv);// 此处char*不能省,因为cpu访问内存读取数据,必须要约束类型
}int main(int argc, char const *argv[])
{pid_t pid = getpid(); // 获取进程号pthread_t pt1 = pthread_self(); // 获取当前进程的主线程号printf("进程:%u,主线程:%lu\n", pid, pt1);char buff[10] = "hello";pthread_t pt2;int ret = pthread_create(&pt2, NULL, func, buff); // 创建线程// int ret =pthread_create(&pt1,NULL,func,(void *)buff);// 以上两种都可以,因为地址本身没有类型,char * void*都可以,只是地址而已if (ret != 0){perror("pthread_create");return 0;}// 在此处阻塞,避免线程未执行,进程就结束,进程结束,系统会回收进程资源// 线程之间是并发的,而不是简单的从上往下执行sleep(1);return 0;
}
线程函数传参:整数
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>void *func(void *argv)
{*(int *)argv = 20;
}int main(int argc, char const *argv[])
{pthread_t pt1 = pthread_self();int num = 10;int ret = pthread_create(&pt1, NULL, func, &num);// int ret =pthread_create(&pt1,NULL,func,(void *)num);// 以上两种都可以,因为地址本身没有类型,int * void*都可以,只是地址而已if (ret != 0){perror("pthread_create");return 0;}sleep(1);printf("num=%d\n", num);return 0;
}
4.2.3 多线程创建
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
pthread_t pt1, pt2;
void *func1(void *argv)
{int count = 1;while (1){printf("线程:%lu:%s,执行了%ds\n", pt1, (char *)argv, count);count++;if (count == 5){break;}}
}
void *func2(void *argv)
{int count = 0;while (1){printf("线程:%lu:%s,执行了%ds\n", pt2, (char *)argv, count);count++;if (count == 5){break;}}
}int main(int argc, char const *argv[])
{pt1 = pthread_self();pt2 = pthread_self();int ret1 = pthread_create(&pt1, NULL, func1, NULL);if (ret1 != 0){perror("pthread_create");return 0;}int ret2 = pthread_create(&pt2, NULL, func2, NULL);if (ret2 != 0){perror("pthread_create");return 0;}sleep(1);return 0;
}
4.3.4 pthread_join()
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
函数功能:
- 等待线程结束(此函数会阻塞),并回收线程资源,类似进程的 wait() 函数。如果线程已经结束,那么该函数会立即返回。
参数:
- thread:被等待的线程号。
- retval:用来存储线程退出状态的指针的地址,二级指针
返回值:
- 成功:0
- 失败:非 0
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
pthread_t pt1, pt2;
void *func1(void *argv)
{int count = 1;while (1){printf("线程:%lu:%s,执行了%ds\n", pt1, (char *)argv, count);count++;if (count == 5){break;}}return "func1";
}
void *func2(void *argv)
{int count = 0;while (1){printf("线程:%lu:%s,执行了%ds\n", pt2, (char *)argv, count);count++;if (count == 5){break;}}
}int main(int argc, char const *argv[])
{pt1 = pthread_self();pt2 = pthread_self();int ret1 = pthread_create(&pt1, NULL, func1, NULL);if (ret1 != 0){perror("pthread_create");return 0;}int ret2 = pthread_create(&pt2, NULL, func2, NULL);if (ret2 != 0){perror("pthread_create");return 0;}void *returnvalue;printf("returnvalue:%p\n",returnvalue);pthread_join(pt1,&returnvalue);//pt1 返回地址是 "func1" 这个字符串的首地址printf("pt1退出状态:%s\n",(char *)returnvalue);return 0;
}
栈区数据不会共享,返回栈区数据导致段错误
函数返回栈区数据段错误示例
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
pthread_t pt1, pt2;void *func2(void *argv)
{int num=10;int count = 0;while (1){printf("线程:%lu:%s,执行了%ds\n", pt2, (char *)argv, count);count++;if (count == 5){break;}}return (void *)#
}int main(int argc, char const *argv[])
{pt2 = pthread_self();int ret2 = pthread_create(&pt2, NULL, func2, NULL);if (ret2 != 0){perror("pthread_create");return 0;}void *returnvalue;printf("returnvalue:%p\n",returnvalue);pthread_join(pt2,&returnvalue);//pt2 返回地址是 "func2" 这个字符串的首地址printf("pt2退出状态:%s\n",(char *)returnvalue);return 0;
}
返回堆区地址:
#include <pthread.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>void *func1(void *argv);
void *func2(void *argv);int main(int argc, char const *argv[])
{pthread_t pt1, pt2;int ret1 = pthread_create(&pt1, NULL, func1, NULL);if (ret1 != 0){perror("pthread_creat()");}int ret2 = pthread_create(&pt2, NULL, func2, NULL);if (ret2 != 0){perror("pthread_creat()");}void *returnvalue;void *returnvalue2;pthread_join(pt1, &returnvalue); // pt1 返回地址是 "pthread_func1" 这个字符串的首地址pthread_join(pt2, &returnvalue2); // printf("pt1退出状态取值:%s\n", (char *)returnvalue); //强转,取字符串 结果 "pthread_func1"printf("pt2退出的地址取值:%d\n",*(int *)returnvalue2);return 0;
}void *func1(void *argv)
{int count = 1;while (1){printf("当前所在进程%u,当前线程%lu,执行了%dS\n", getpid(), pthread_self(), count);sleep(1);count++;if (count >= 6){break;}}return "pthread_func1";
}void *func2(void *argv)
{int *p = (int *)malloc(sizeof(int));*p = 10;int count = 1;while (1){printf("当前所在进程%u,当前线程%lu,执行了%dS\n", getpid(), pthread_self(), count);sleep(1);count++;if (count >= 6){break;}} return p; //不能是栈区地址,因为栈区地址不共享,每一个线程都有自己的栈
}
4.3.5pthread_detach()
一般情况下,线程终止后,其终止状态一直保留到其它线程调用 pthread_join 获取它的状态为止。但是线程也可以被置为 detach 状态,这样的线程一旦终止就立刻回收(系统回收)它占用的所有资源,而不保留终止状态。
所以如果回收线程资源: pthread_join与pthread_detach只能二选一
#include <pthread.h>
int pthread_detach(pthread_t thread);
函数功能:
- 将指定线程标记为分离状态。
参数:
- pthread :指定的要分离的线程
4.3.6 pthread_exit()
#include <pthread.h>
void pthread_exit(void *retval);
函数功能:
- 退出调用线程。一个进程中的多个线程是共享该进程的数据段,因此,通常线程退出后所占用的资源并不会释放。
参数:
- retval:存储线程退出状态的指针。
#include <pthread.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#define sucess 0
#define error 1void *pthread_func1(void *argv);int main(int argc, char const *argv[])
{pthread_t pt1;char buff[] = "hello my thread";int ret = pthread_create(&pt1, NULL, pthread_func1, buff); // 创建线程,线程函数传参printf("pt1=%lu\n", pt1);if (ret != 0){perror("pthread_create())");return 0;}printf("在进程%u中,主线程号:%lu\n", getpid(), pthread_self());void *return_value;pthread_join(pt1, &return_value);if (return_value == NULL) // 0{printf("success\n");}else{printf("error\n"); // 1}// free(return_value);return 0;
}void *pthread_func1(void *argv)
{for (int i = 1; i <= 5; i++){printf("线程%lu,执行了%dS\n", pthread_self(), i);sleep(1);if (i == 5){pthread_exit((void *)sucess); // void * + 数值,直接转换为地址}}return NULL;
}
4.3.7 pthread_cancel()
#include <pthread.h>
int pthread_cancel(pthread_t thread);
函数功能:
- 杀死(取消)线程
参数:
- thread : 目标线程 ID。
返回值:
- 成功:0
- 失败:出错编号
线程的取消并不是实时的,而有一定的延时。
需要等待线程到达某个取消点。
取消点:执行命令 man 7 pthreads 可以查看具备这些取消点的系统调用列表。 可粗略认为一个系统调用(进入内核)即为一个取消点,例如sleep();
#include <pthread.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>void *myfunc1(void *argv);int main(int argc, char const *argv[])
{pthread_t pt1;int ret = pthread_create(&pt1,NULL,myfunc1,NULL);pthread_cancel(pt1);while (1){/* code */}return 0;
}void *myfunc1(void *argv)
{while (1){printf("当前所在进程%u,当前线程%lu\n",getpid(),pthread_self());sleep(1);printf("check\n"); //cancel完不会执行,因为上面有取消点}}
5.线程的属性
typedef struct
{
int etachstate; //线程的分离状态
int schedpolicy; //线程调度策略
struct sched_param schedparam; //线程的调度参数
int inheritsched; //线程的继承性
int scope; //线程的作用域
size_t guardsize; //线程栈末尾的警戒缓冲区大小
int stackaddr_set; //线程的栈设置
void* stackaddr; //线程栈的位置
size_t stacksize; //线程栈的大小
} pthread_attr_t;
提前设置分离属性
- 先进行属性初始化
- 设置某一个属性
- 创建线程
- 销毁线程属性
5.1 pthread_attr_init()
int pthread_attr_init(pthread_attr_t *attr);
函数功能:
- 对线程属性结构体 attr初始化
参数:
- attr 要初始化的结构体
5.2 pthread_attr_setdetachstate()
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
函数功能:
- 线程分离属性设置
参数:
- attr:已初始化的线程属性
- detachstate:分离状态
PTHREAD_CREATE_DETACHED(分离线程)
PTHREAD_CREATE_JOINABLE(非分离线程)
5.3
int pthread_attr_destroy(pthreadattrt *attr);
函数功能:
- 线程销毁属性设置
参数:
- attr:要销毁的线程属性
函数返回值:
- 成功:0;
- 失败:错误号
#define _POSIX_SOURCE
#include <pthread.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>void *myfunc1(void *argv);int main(int argc, char const *argv[])
{pthread_t pt1;pthread_attr_t attr;// 初始化pthread_attr_init(&attr);// 设置分离属性pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);// 创建线程int ret = pthread_create(&pt1, &attr, myfunc1, NULL);// destorypthread_attr_destroy(&attr);while (1){/* code */}return 0;
}void *myfunc1(void *argv)
{while (1){printf("当前所在进程%u,当前线程%lu\n", getpid(), pthread_self());sleep(1);}
}