线程的相关概念及线程控制

本节重点:
1.线程的基本概念,线程与进程的区别与联系。
2.线程id与进程id。
3.线程控制(线程创建、线程等待、线程终止)。
4.线程分离。

————————————————————————————————————————————————
1、线程的基本概念,线程与进程的区别与联系。

  • 线程的概念:
    <1>.线程是调度的基本单位、线程是一个进程内部的控制序列。
    <2>.线程的执行力度比进程更细致、在进程的地址空间内运行。
    <3>.一个进程至少有一个执行线程。

  • 线程与进程的区别与联系:
    <1>.进程是承担分配系统资源的基本单位。是资源竞争的基本单位。
    <2>.线程是调度的基本单位,是程序执行的最小单位。
    <3>.线程共享进程数据。共享的数据如下:
    a.由于同一进程的多个线程共享同一地址空间,所以代码段,数据段是共享的,如果定义一个函数(存储在代码段),各线程都可以进行调用,如果定义个全局变量(存储在数据段),在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:
    b.文件描述符表
    c.每种信号的处理方式(SIG_IGN,SIG_DFL,用户自定义)
    d.当前工作目录
    e.用户id和组id

    <4>.但是有自己的一部分数据是独享的
    a.线程ID
    b.一组寄存器
    c.栈:线程运行时会形成临时变量,但要保证线程间不被干扰。
    d.信号屏蔽字
    e.硬件上下文:包括各种寄存器的值,程序计数器和栈指针。
    f.调度优先级
    g.error

  • 线程与进程的联系:
    分为单线程进程、多个单线程进程。
    单进程多线程、多个多线程进程。

2.线程id与进程id。
在Linux中,线程又被称为轻量级进程(LWP),每一个用户态的线程,在内核中都对应一个调度实体,也有有自己的一张进程描述符(task_struct)结构体。
多线程的进程,又称为线程组,线程组内的每一个线程在内核中都存在一个进程描述符,与之对应。原因是Linux下的线程是由进程模拟来的。
进程描述符结构体中的pid,表面上看对应的是进程id,其实它对应的是线程id,进程描述符中的tgid,该值对应的是用户层面的进程id.

- 线程id:是pid_t类型的变量,是唯一用来标识线程的一个整型变量,即为LWP
这里写图片描述
LWP:线程id,是gettid()系统调用的返回值。
-当然还有一个函数也是返回线程的id:pthread_t pthread_self(void);
对于目前实现的POSIX下的线程来说,pthread_t类型的线程id,本质是一个进程地址空间上的一个地址。

好像有点绕。下面我就这两个函数进行说明两者的区别:

Linux中,每个进程有一个pid,类型pid_t,由getpid()取得。Linux下的POSIX线程也有一个id,类型 pthread_t,由pthread_self()取得,该id由线程库维护,其id空间是各个进程独立的(即不同进程中的线程可能有相同的id)。Linux中的POSIX线程库实现的线程其实也是一个进程(LWP),只是该进程与主进程(启动线程的进程)共享一些资源而已,比如代码段,数据段等。原因是Linux下的线程是由进程模拟的。

  • linux多线程环境下gettid() pthread_self() 两个函数都获得线程ID,可它们的返回值不一样。
  • linux使用进程模拟线程,gettid 函数返回实际的进程ID(内核中的线程的ID).
  • pthread_self 函数返回 pthread_create创建线程时的ID(POSIX thread ID).pthread_t类型的线程id,本质是一个进程地址空间上的一个地址。

3.线程控制(线程创建、线程等待、线程终止)。
(1).创建线程: pthread_create函数

  • 函数原型:
    int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start_routine)(void*),void*arg); `

  • 函数功能:创建一个新的线程。

  • 参数解释:
    thread:返回线程id.
    attr:设置线程属性,attr为NULL表示使用,默认属性。
    start_routine:是一个函数地址,线程启动后要执行的函数。
    arg:传给线程启动的参数。
  • 返回值:成功返回0,失败返回错误码。

(2).线程终止:
- 线程终止的三种方式:

  • 1.从线程函数中return,但是该方式对主线程不适用,从main函数中return相当于调用exit,使得进程推出。
  • 2.线程可以调用pthread_exit函数终止自己。
  • -3. 一个线程可以调用pthread_cancel函数终止同一进程中的另一个线程。

-函数介绍

  • phread_exit函数:<1>.函数原型:
void pthread_exit(void*value_ptr);

<2>.函数功能:线程终止。
<3>.参数解释:value_ptr不要指向一个局部变量。
<4>.返回值:无返回值。
pthread_exit或者return 返回的指针所指向的内存单元必须是全局的或者是用malloc进行分配的,不能在线程函数栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。
-pthread_cancel函数:<1>.函数原型:

int pthread_cancel(pthread_t thread);

<2>.函数功能:取消一个执行中的线程。他杀,杀掉另一个线程。
cancel后,线程不会立刻退出,而是要等到取消点才退出。
取消点:简单记成凡是系统调用的地方都是取消点。
<3>.参数解释:thread:线程id.
<4>.返回值:成功返回0,失败返回错误码。

(3).线程等待:

-等待的原因:已经退出的线程,其空间没有被释放,仍然在进程的地址空间内,所以要回收资源。
创建新的线程不会复用刚才推出线程的地址空间。
只有阻塞式等待,住线程等待线程结束。

  • 函数原型:int pthread_join(phtread_t thread,void**value_ptr);

  • 参数解释:thread:线程ID,
    - value_ptr:它指向一个指针,后者指向线程的返回值。
    - 如果线程通过return返回,value_ptr所指向的单元里存放的是thread线程函数的返回值。
    - 如果线程是被别的线程cancel掉,value_ptr所指向的单元里存放的是常数PTHREAD_CANCELED。
    - 如果线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。
    - 如果对线程的终止状态不感兴趣,可以传NULL给value_ptr参数。

  • 返回值:成功返回0,失败返回错误码。

    下面来看代码实例:(pthread1.c)
    1.从线程函数中return。

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<pthread.h>
  4 
  5 void *thread_run(void *arg)
  6 {
  7         printf("thread is running,pid:%d,thread:%d\n",getpid(),pthread_self());
  8         sleep(3);
  9         return (void*)3;
 10 }
 11 int main()
 12 {
 13         pthread_t t;
 14         pthread_create(&t,NULL,thread_run,NULL);
 15         printf("main thread is running!pid:%d,thread:%d\n",getpid(),pthread_self());
 17         void *code;
 18         int ret=pthread_join(t,&code);//线程等待
 19         if(ret==0){
 20         printf("wait new thread sucess,%d\n",(int)code);
 21         }
 22 }
~                   

结果显示:
这里写图片描述

2.线程可以调用pthread_exit函数终止自己。

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<pthread.h>
  4 
  5 void *thread_run(void *arg)
  6 {
  7         printf("thread is running,pid:%d,thread:%d\n",getpid(),pthread_self());
  8         sleep(3);
 10         pthread_exit((void*)0);
 11 }
 12 int main()
 13 {
 14         pthread_t t;
 15         pthread_create(&t,NULL,thread_run,NULL);
 16         printf("main thread is running!pid:%d,thread:%d\n",getpid(),pthread_self());
 17         //pthread_cancel(t);
 18         void *code;
 19         int ret=pthread_join(t,&code);
 20         if(ret==0){
 21         printf("wait new thread sucess,%d\n",(int)code);
 22         }
 23 }
~                 

结果显示:
这里写图片描述
3.主线程调用pthread_cancel函数终止另一个线程。

 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<pthread.h>
  4 
  5 void *thread_run(void *arg)
  6 {
  7         printf("thread is running,pid:%d,thread:%d\n",getpid(),pthread_self());
  8         sleep(3);
  9 }
 10 int main()
 11 {
 12         pthread_t t;
 13         pthread_create(&t,NULL,thread_run,NULL);
 14         printf("main thread is running!pid:%d,thread:%d\n",getpid(),pthread_self());
 15         pthread_cancel(t);
 16         void *code;
 17         int ret=pthread_join(t,&code);
 18         if(ret==0){
 19         printf("wait new thread sucess,%d\n",(int)code);
 20         }
 21 }

结果显示:
这里写图片描述
4.线程分离。
线程的分离状态决定一个线程是以一个什么样的方式来终止自己,线程的默认属性一般是可结合的。

  • 默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_jion操作,否则无法释放资源,从而导致内存泄漏。
  • 如果不关心线程的返回值,利用分离函数,一旦线程被设置为分离状态,代表该线程结束后不需要主动进行线程等待回收,其退出时,会资源自动释放。。。

函数原型:

int pthread_detach(pthread_t thread);

参数解释:thread:线程id.
线程组内其它线程对目标线程进行分离,也可以是线程自己分离。

只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的资源。
而分离线程没有被其他线程进行等待,自己运行结束了,线程也就终止了,其会立刻释放自己占用的资源。

下面用代码实现(pthread2.c)

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<pthread.h>
  4 
  5 void *thread_run(void*arg)
  6 {
  7         char*a=(char*)arg;
  8         pthread_detach(pthread_self());//自己已经将自己分离了
  9         printf("%s\n",a);
 10         return NULL;
 11 }
 12 int main()
 13 {
 14         pthread_t tid;
 15         pthread_create(&tid,NULL,thread_run,"thread run");
 16         int code=0;
 17         sleep(1);
 18         int ret=pthread_join(tid,NULL);
 19         if(ret==0){
 20                 printf("thread wait success\n");
 21                 code=0;
 22         }else{
 23                 printf("thread wait error\n");
 24                 code=1;
 25         }
 26         return code;
 27 }
 28 
~                        

所以该程序的理论结果应该是输出thread wait error。
让我们来看看运行结果:
这里写图片描述

好了,这篇博客就先讲这么多,线程同步与互斥在下篇博客中呈现。

猜你喜欢

转载自blog.csdn.net/xiaodu655/article/details/80315569