线程-----线程概念,线程创建,线程终止,线程等待,线程分离

线程的概念

在生活中,我们在使用电脑时,可以同时使用多个软件,比如:在写代码的同时,还可以听着音乐.即使有一个CPU,也可以同时进行多个任务.
更充分的利用多核CPU:可以将一个任务分给几个CPU然后同时进行.
线程:是一个进程内部的”控制序列”.
任意进程至少有一个线程.

特点:
1.线程是操作系统进行调度的最小单位.
2.一条线程指的是一个单一顺序的控制流(一个进程中可以有多个控制流)
3.一个进程可以并发多个线程,每条线程并行执行不同的任务.

进程和线程的区别

1.资源所有权
进程(或者任务)用于管理和分配资源.
线程(或轻量级进程)是调度和执行的基本单位.(所以在Linux中,描述线程的也是PCB).

进程:也叫线程组.
线程:也可叫轻量级进程.

进程和线程的关系图
在一个进程中可以有多个线程,而每个线程都是平等的,只有一个主线程即main函数所在线程.在每个线程中,都有自己的调用栈和上下文,而虚拟内存中的栈是主线程的调用栈指的是主线程的调用栈,而其他线程的调用栈则存在于共享内存中.
如下图所示:

线程组

多线程的进程就叫做线程组.线程组中的每一个线程在内核之中都存在一个进程描述符(task_struct)与之对应.

进程:task_struct = 1:N
task_struct
{
    ...
    pid_t pid;(线程ID,即PCB的ID)
    pid_t tgid;(线程组PID)(getpid()得到的就是tgid)
    ...
    struct task_struct *group_leader;(指向主线程的指针)
    ...
};

主线程中的pid和tgid相同.tgid是线程组ID,而pid是每个线程的自己的ID.

线程间数据的共享和独立的数据

共享:
1.虚拟地址空间(即使用同一块页表)
2.文件描述符表
3.信号处理方式.
各自的独立的数据:
1.调用栈.
2.线程上下文(一组寄存器).
3.线程ID
4.errno(函数出错时返回的错误码)
5.信号屏蔽字(未决信号集是共用一个,进程收到一个信号,那么所有的线程都会收到)
6.调度优先级

进程的优缺点

优点:
1.创建一个新线程的代价比创建一个新进程要小的多.
2.线程之间共享数据更容易.
3.线程占用的资源要比进程少得多(只需要为线程提供调用栈,线程上下文等)
4.线程之间的切换比进程之间的切换容易
缺点:
1.编码/调试多线程代码变得困难.(由于共享资源可能会存在大量的时序问题)
2.缺乏访问控制,如果一个线程崩溃,那么其余线程都会终止.如果一个进程调用了kill等函数会影响整个进程.
线程的使用场景
1.CPU密集型应用:即将计算分配到多个线程中实现
2.I/O密集型应用

线程控制

POSIX线程库

1.线程的创建

函数原型:
int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void*(*start_routine),void* arg);
thread:返回线程ID
start_routine:是个函数地址,线程启动执行的函数
arg:线程启动函数的参数
返回值:成功返回1,失败返回0

下面创建了一个线程:

  1 #include <stdio.h>
  2 #include<unistd.h>
  3 #include<pthread.h>
  4 
  5 void* PthreadEntry(void* arg)                                                                                          
  6 {
  7     (void)arg;
  8     while(1)
  9     {
 10         printf("pthread\n");
 11         sleep(1);
 12     }
 13     return NULL;
 14 }
 15 int main()
 16 {
 17     pthread_t pth;
 18     pthread_create(&pth,NULL,PthreadEntry,NULL);
 19     while(1)
 20     {
 21         printf("main pthread\n");
 22         sleep(1);
 23     }
 24     return 0;
 25 }

注意:在gcc中,在生成可执行函数时,要加-lpthread,找库.
执行的结果:创建的线程和主线程(main)不确定的进行执行.
总结:多个线程同时存在时,执行是没有先后顺序的.但是如果主线程退出,那么在同一进程中的其余线程也会终止.

2.线程终止

三种方法可终止线程:

1.从线程函数return.(不适合主线程,main函数返回相当于调用exit函数)
2.线程可以调用pthread_exit来终止自己.
3.可以调用pthread_cancel终止同一个进程中的另一个线程.

pthread_exit函数
void pthread_exit(void *value_ptr);
参数:线程入口的返回值.不要指向一个局部变量.
无返回值,结束时无法返回到调用者.
pthread_cancel函数(强制线程终止)
取消一个执行中的线程
int pthread_cancel(pthread_t thread);
参数:线程ID
返回值:成功返回0,失败返回错误码.

下面学习一下这3中方法来终止线程:

  1 #include <stdio.h>
  2 #include<unistd.h>
  3 #include<pthread.h>
  4                                                                                                                      
  5 void *PthreadEntry1(void* arg)
  6 {
  7     (void)arg;
  8     printf("P1\n");
  9     return (void*)1;
 10 }
 11 void *PthreadEntry2(void* arg)
 12 {
 13     (void)arg;
 14     printf("P2\n");
 15     pthread_exit((void*)2);
 16     return NULL;
 17 }
 18 void *PthreadEntry3(void* arg)
 19 {
 20     (void)arg;
 21     printf("P3\n");
 22     pthread_cancel(pthread_self());                                                                                  
 23     return NULL;
 24 }
 25 int main()
 26 {
 27     pthread_t p1,p2,p3;
 28     pthread_create(&p1,NULL,PthreadEntry1,NULL);
 29     pthread_create(&p2,NULL,PthreadEntry2,NULL);
 30     pthread_create(&p3,NULL,PthreadEntry3,NULL);
 31     void* ret1;
 32     pthread_join(p1,&ret1);
 33     printf("ret1 = %p\n",ret1);
 34     void* ret2;
 35     pthread_join(p2,&ret2);
 36     printf("ret2 = %p\n",ret2);
 37     void* ret3;
 38     pthread_join(p3,&ret3);
 39     printf("ret3 = %p\n",ret3);
 40     return 0;
 41 }

执行结果:
这里写图片描述

3.线程等待

等待的原因:(为了回收资源)

如果退出的线程,没有进行资源释放,仍然在进程的地址空间中.类似于僵尸进程
那么再次创建的新线程就不可以使用退出的进程的资源.

与进程等待的区别:
1.进程只可以父进程等待子进程
2.同一个进程中的线程,都可以等待其他的线程.
3.进程等待有阻塞和非阻塞.而线程等待中,只有一个阻塞等待.

int pthread_join(pthread_t thread,void **value_ptr);
thread:线程ID
value_ptr:指向一个线程的返回值
返回值:成功返回0,失败返回错误码.

调用等待函数后,该线程就会挂起等待,直到thread线程终止.
线程等待可以决定线程结束,调用哪个就会阻塞式的等待哪个线程,直到回收了对应的资源,才会去等待回收其它的线程.

4.线程分离

新创建的线程如果不对它进行线程等待,避免造成系统资源泄露.并且线程等待pthread_join可以决定线程的结束.
如果,当程序员们不关心线程的结束顺序时.只要线程结束时,可以自动的释放资源.那么就可以用将线程分离,在线程结束时就可以自动的释放资源.
线程等待和分离的区别:
手动(线程等待)还是自动(线程分离)回收资源.

线程分离特点:
既可以分离其它线程也可以分离自身.

充分利用多核CPU:

  1 #include <stdio.h>
  2 #include<unistd.h>
  3 #include<pthread.h>                                                                                                    
  4 
  5 void *Pthread(void* arg)
  6 {
  7     (void)arg;
  8     while(1)
  9     {}
 10     return NULL;
 11 }
 12 int main()
 13 {
 14     pthread_t pth[4];
 15     int i = 0;
 16     for(i = 0;i < 4;++i)
 17     {
 18         pthread_create(&pth[i],NULL,Pthread,NULL);
 19     }
 20     for(i = 0;i < 4;++i)
 21     {
 22         pthread_join(pth[i],NULL);
 23     }
 24     return 0;
 25 }

通过top指令查看的结果:
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值