LINUX 多线程_linuxd的多线程

69 阅读6分钟

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 错误。

 

  1. 该函数还有一个非常重要的作用, 由于一个进程中的多个线程共享数据段, 因此通常在一个线程退出后,退出线程所占用的资源并不会随线程结束而释放。如果 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