1、概述
进程是系统中程序执行和资源分配的基本单位。每个进程有自己的数据段、代码段和堆栈段。故当在进行上下文切换时,开销较大,于是出现了线程。线程通常叫做轻量级进程,线程是是进程的基本调度单元,每个进程至少都有一个 main 线程。它与同进程中的其他线程共享进程空间{堆 代码 数据 文件描述符 信号等},只拥有自己的栈空间,大大减少了上下文切换的开销。
线程和进程在使用上各有优缺点:线程执行开销小,占用的 CPU 少,线程之间的切换快,但不利于资源的管理和保护;而进程正相反。从可移植性来讲,多进程的可移植性要好些。要注意的是,由于线程共享了进程的资源和地
址空间,因此,任何线程对系统资源的操作都会给其他线程带来影响。
2、线程分类
按调度者分为用户级线程和核心级线程
- 用户级线程:主要解决上下文切换问题,调度算法和调度过程全部由用户决定,在运行时不需要特定的内核支持。缺点是无法发挥多处理器的优势。
- 核心级线程:允许不同进程中的线程按照同一相对优先调度方法调度, 发挥多处理器的并发优势。
3、Linux下的线程
Linux 的线程是通过用户级的函数库实现的,一般采用 pthread 线程库实现线程的访问和控制。它用第3方 posix 标准的 pthread,具有良好的可移植性。编译的时候要在后面加上 –lpthread 。
创建 退出 等待
多进程 fork() exit() wait()
多线程 pthread_create pthread_exit() pthread_join()
注意的是,在使用线程函数时,不能随意使用exit退出函数进行出错处理, 由于 exit 的作用是使调用进程终止,往往一个进程包括了多个线程,所以在线程中通常使用 pthread_exit 函数来代替进程中的退出函数 exit。
函数原型:
#include <pthread.h>
int pthread_create(pthread_t* thread, pthread_attr_t * attr, void *(*start_routine)(void *), void * arg);
void pthread_exit(void *retval);
通常的形式为:
pthread_t pthid;
pthread_create(&pthid,NULL,pthfunc,NULL);或
pthread_create(&pthid,NULL,pthfunc,(void*)3);
pthread_exit(NULL);或 pthread_exit((void*)3);
//3 作为返回值被 pthread_join 函数捕获。
函数pthread_create用来创建线程。返回值:成功,则返回0;失败,则返回对应错误码。
各参数描述如下:
- 参数 thread 是传出参数,保存新线程的标识;
- 参数 attr 是一个结构体指针,结构中的元素分别指定新线程的运行属性,attr可以用 pthread_attr_init 等函数设置各成员的值,但通常传入为 NULL 即可;
- 参数 start_routine 是一个函数指针,指向新线程的入口点函数, 线程入口点函数带有一个 void *的参数由 pthread_create 的第 4 个参数传入;
- 参数 arg 用于传递给第 3 个参数指向的入口点函数的参数, 可以为 NULL,表示不传递。
函数 pthread_exit 表示线程的退出。 其参数可以被其它线程用 pthread_join 函数捕获。
eg:
#include <stdio.h>
#include <pthread.h>
void *ThreadFunc(void *pArg) //参数的值为 123
{
int i = 0;
for(; i<10; i++)
{
printf("Hi,I'm child thread,arg is:%d\n", (int)pArg);
sleep(1);
}
pthread_exit(NULL);
}
int main()
{
pthread_t thdId;
pthread_create(&thdId, NULL, ThreadFunc, (void *)123 );
int i = 0;
for(; i<10; i++)
{
printf("Hi,I'm main thread,child thread id is:%x\n", thdId);
sleep(1);
}
return 0;
}
编译时需要带上线程库选项: gcc -o a a.c -lpthread
3. 线程的等待退出
线程从入口点函数自然返回,或者主动调用 pthread_exit()函数,都可以让线程正常终止线程从入口点函数自然返回时,函数返回值可以被其它线程用 pthread_join 函数获取。
pthread_join 原型为:
#include <pthread.h>
int pthread_join(pthread_t th, void **thread_return);
1.该函数是一个阻塞函数, 一直等到参数 th 指定的线程返回; 与多进程中的 wait 或 waitpid 类似。thread_return 是一个传出参数, 接收线程函数的返回值。 如果线程通过调用 pthread_exit()终止, 则
pthread_exit()中的参数相当于自然返回值, 照样可以被其它线程用 pthread_join 获取到。
2.thid 传递 0 值时,join 返回 ESRCH 错误。
- 该函数还有一个非常重要的作用, 由于一个进程中的多个线程共享数据段, 因此通常在一个线程退出后,退出线程所占用的资源并不会随线程结束而释放。如果 th 线程类型并不是自动清理资源类型的,则 th 线程退出后,线程本身的资源必须通过其它线程调用 pthread_join 来清除,这相当于多进程程序中的 waitpid。
3、线程的取消(cancel)
线程取消的方法是一个线程向目标线程发 cancel 信号, 但是如何处理 cancel 信号则由目标线程自己决定,目标线程或者忽略、或者立即终止、或者继续运行至 cancelation-point(取消点)后终止。
#include <pthread.h>
int pthread_cancel(pthread_t thread);
取消点:
根据 POSIX 标准, pthread_join()、 pthread_testcancel()、 pthread_cond_wait()、pthread_cond_timedwait()、 sem_wait()、 sigwait()等函数以及 read()、 write()等会引起阻塞的系统调用都是 Cancelation-point, 而其他 pthread 函数都不会引起 Cancelation 动作。
4、线程的终止清理函数
线程为了访问临界共享资源而为其加上锁, 但在访问过程中该线程被外界取消, 或者发生了中断, 则该临界资源将永远处于锁定状态得不到释放。 外界取消操作是不可预见的, 因此的确需要一个机制来简化用于资源释放的编程。
在 POSIX 线程 API 中提供了一个 pthread_cleanup_push()/pthread_cleanup_pop()函数对用于自动释放资源--从 pthread_cleanup_push()的调用点到 pthread_cleanup_pop()之间的程序段中的终止动作都将执行 pthread_cleanup_push()所指定的清理函数。
- void pthread_cleanup_push(void (*routine) (void *), void *arg)
- void pthread_cleanup_pop(int execute)
- pthread_cleanup_push()/pthread_cleanup_pop()采用先入后出的栈结构管理
pthread_cleanup_pop 的参数 execute
如果为非 0 值,则按栈的顺序注销掉一个原来注册的清理函数,并执行该函数; 当其为 0 时,仅仅在线程调用 pthread_exit 函数或者其它线程对本线程调用 pthread_cancel 函数时,才在弹出“清理函数”的同时执行该“清理函数”。
5、线程的互斥
因为多个线程共用进程的资源, 要访问的是公共区间时(全局变量)。故为了使其各个线程互斥访问临界资源,故需要加锁。在 Posix Thread 中定义了一套专门用于线程互斥的 mutex 函数。
创建 互斥锁:
动态方式是采用 pthread_mutex_init()函数来初始化互斥锁, API 定义如下:
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
其中 mutexattr 用于指定互斥锁属性(见下) , 如果为 NULL 则使用缺省属性。 通常为 NULL。
pthread_mutex_destroy()用于注销一个互斥锁,API 定义如下:
int pthread_mutex_destroy(pthread_mutex_t *mutex);
最后的话
最近很多小伙伴找我要Linux学习资料,于是我翻箱倒柜,整理了一些优质资源,涵盖视频、电子书、PPT等共享给大家!
资料预览
给大家整理的视频资料:
给大家整理的电子书资料:
如果本文对你有帮助,欢迎点赞、收藏、转发给朋友,让我有持续创作的动力!
详情docs.qq.com/doc/DSmdCdUNwcEJDTXFK