文章目录
- ❓线程是什么
- 🚀为什么要在程序中使用线程
- 🍎线程的优点、缺点
- 🎂线程的应用场合
- 🌰线程的基本使用
- ⭐创建线程
- ⭐线程的终止
- ⭐等待指定线程结束
- ⭐线程程序的编译命令
- 🏠线程使用案例
❓线程是什么
首先我们要知道进程是程序的一次执行过程也是系统进行资源分配和调度的基本单位,而线程是进程中实施资源调度和分派的基本单位。
,也就是说一个进程中至少有一个线程(即使不额外使用线程,进程内部也有一个执行线程。)。
🚀为什么要在程序中使用线程
- 在程序中使用fork创建进程以执行新的任务,该方式的代价很高(主要体现在资源复制这方面)。
- 多个进程间不会直接共享内存(进程间是独立的)
- 线程是进程的基本执行单元,一个进程的所有任务都在线程中执行,进程要想执行任务,必须得有线程,进程至少要有一条线程,程序启动会默认开启一条线程,这条线程被称为主线程或 UI 线程
场景类比,如果我们创建进程就等同于将家庭A复制成家庭B(资源都是一样的),但是现在A,B两个家庭之间是没有联系的,而如果是创建线程就等同于在家庭A中增加一个成员(家庭还是原来的家庭,我们可以一起共享家庭A的各种资源,同一个进程内的各个线程,能够共享整个进程的全局变量,除了线程的局部变量外,其他资源都共享
)
注意:
单核处理器上,同一个时刻只能运行一个线程,但是对于用户而言,感觉如同同时执行了多个线程一样(各线程在单核CPU上切换,在一段时间内,同时执行了多个线程)
🍎线程的优点、缺点
优点: 创建线程比创建进程,开销要小。
缺点:
- 多线程编程,需特别小心,很容易发生错误。
- 多线程调试很困难。
- 把一个任务划分为两部分,用两个线程在单处理器上运行时,不一定更快。 除非能确定这两个部分能同时执行、且运行在多处理器上。
🎂线程的应用场合
-
需要让用户感觉在
同时
做多件事情时,比如,处理文档的进程,一个线程处理用户编辑,一个线程同时统计用户的字数。 -
当一个应用程序,需要
同时
处理输入、计算、输出时,可开3个线程,分别处理输入、计算、输出。让用户感觉不到等待。 -
高并发编程。
🌰线程的基本使用
⭐创建线程
原型:int pthread_create (
pthread_t *thread,
pthread_attr_t *attr,
void *(*start_routine)(void*),
void *arg);
参数解析:
参数: thread, 指向新线程的标识符。
通过该指针返回所创建线程的标识符。
attr, 用来设置新线程的属性。
一般取默认属性,即该参数取NULL
start_routine, 该线程的处理函数
该函数的返回类型和参数类型都是void*
arg, 线程处理函数start_routine的参数
功能: 创建一个新线程,
同时指定该线程的属性、执行函数、执行函数的参数
通过参数1返回该线程的标识符。
返回值: 成功,返回0
失败,返回错误代码
注意:大部分pthread_开头的函数成功时返回0,失败时返回错误码(而不是-1)
⭐线程的终止
原型:void pthread_exit (void *retval)
参数说明:
功能: 在线程函数内部调用该函数。
终止该线程,并通过参数retval返回一个指针。(把其他线程想要的东西放在retval中)
该指针不能指向该线程的局部变量。
⭐等待指定线程结束
原型:int pthread_join(pthread_t th,void**thread_return);
参数说明:
参数: th, 指定等待的线程
thread_return, 指向该线程函数的返回值(就是用来接收pthread_exit(void*retval)中的retval)
线程函数的返回值类型为void*,故该参数的类型为void**
功能: 类似与进程中的waitpid等待指定的线程结束,
并使参数指向该线程函数的返回值(用pthread_exit返回的值)
返回值:成功返回0
失败返回错误编号
⭐线程程序的编译命令
(1) 编译时,定义宏_REENTRANT#可重入定义
(2) 编译时,指定线程库,即: gcc -lpthread
总结:一般使用如下形式即可 gcc <你的.c文件> -D_REENTRANT -lpthread -o <你想要生成的可执行文件的名字>
举例:
gcc main.c -D_REENTRANT -lpthread -o main.exe
🏠线程使用案例
编译命令: gcc main.c -D_REENTRANT -lpthread -o main.exe
main.c
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
int my_global;
void* my_thread_handle(void *arg)
{
int val;
val = *((int*)arg);
printf("new thread begin, arg=%d\n", val);
my_global += val;
sleep(3);
//退出程序,并将my_global传出去(记住my_globle不可是该线程中的临时变量)
pthread_exit(&my_global);
//不会到这一步
printf("new thread end\n");
}
int main(void)
{
pthread_t mythread;
int arg;
int ret;
void *thread_return;
arg = 100;
my_global = 1000;
printf("my_global=%d\n", my_global);
printf("ready create thread...\n");
//创建线程,线程标识符为mythread,线程执行函数为my_thread_handle,函数所带的参数是arg
ret = pthread_create(&mythread, 0, my_thread_handle, &arg);
if (ret != 0) {
printf("create thread failed!\n");
exit(1);
}
printf("wait thread finished...\n");
//利用thread_return将mythread线程退出时带的数据接住
ret = pthread_join(mythread, &thread_return);
if (ret != 0) {
printf("pthread_join failed!\n");
exit(1);
}
printf("wait thread end, return value is %d\n", *((int*)thread_return));
printf("my_global=%d\n", my_global);
printf("create thread finished!\n");
}
运行结果:
感谢您看到这了,希望这篇文章对您有帮助,如果手不累的话可以点一点下方的大拇指(*^_^*),这对我是莫大的鼓励,当然,如果对于相关知识点有不同见解的同学也可以在评论区中提出来哟,相互学习学习😀