目录
线程
线程基本原理
进程fork,子进程会复制(读时共享、写时复制)父进程的虚拟内存空间,从逻辑控制流的角度来说,fork创建的子进程开始执行的位置是fork函数返回的位置。
上下文切换:(子进程复制了父进程的各种(文件描述符表,信号控制信息等信息),故要进行这些信息+进程运行时信息(寄存器、进程运行信息)的备份。
进程上下文切换保存的内容有:
页表 -- 对应虚拟内存资源。(父子进程内存独立)
文件描述符表/打开文件表 -- 对应打开的文件资源(与进程相关的数据结构)
寄存器 -- 对应运行时数据
进程运行信息。
线程:
并发的本质是同时运行的多个逻辑流。并发编程要解决的一个很重要的问题就是对资源的并发访问的问题。而两个进程恰恰很难在逻辑上表示共享资源(进程间通信)。
一个进程的所有线程都是共享这个进程的同一个虚拟地址空间的,从线程的角度它们看到的物理资源是一样的,这样就可以通过共享变量的方式来表示共享资源,也就是直接共享内存的方式解决了线程通信的问题。而线程也表示一个独立的逻辑流,支持并发。
进程上下文切换(线程运行时的数据):线程的id、寄存器中的值、栈数据
从这个角度,线程更像寄存器+栈。
与进程的区别
进程是资源分配的最小单位,线程是CPU调度的最小单位。
2)进程有独立的系统资源,而同一进程内的线程共享进程的大部分系统资源,包括堆、代码段、数据段,每个线程只拥有一些在运行中必不可少的私有属性,比如tcb,线程Id,栈、寄存器。
3)一个进程崩溃,不会对其他进程产生影响;而一个线程崩溃,会让同一进程内的其他线程也死掉。
4)进程在创建、切换和销毁时开销比较大,而线程比较小。进程创建的时候需要分配系统资源,而销毁的的时候需要释放系统资源。进程切换需要分两步:切换页目录、刷新TLB以使用新的地址空间;切换内核栈和硬件上下文(寄存器);而同一进程的线程间逻辑地址空间是一样的,不需要切换页目录、刷新TLB。
5)进程间通信比较复杂,而同一进程的线程由于共享代码段和数据段,所以通信比较容易。
线程原语:
线程创建:
函数返回,当前线程继续往下执行,新线程从传入的函数指针start_routine开始执行。函数返回时则线程退出。
获取调用线程tid
线程退出:
线程回收:
线程取消:
线程分离:
比较两个线程是否相等:
线程终止方式:
线程安全:
- 如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
- 线程安全问题都是由全局变量及静态变量引起的。
- 若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。
- 对于线程不安全的对象我们可以通过如下方法来实现线程安全:
线程同步
互斥量(锁):
锁机制是同一时刻只允许一个线程执行一个关键部分的代码。
死锁:
读写锁:
条件变量
条件变量是利用线程间共享全局变量进行同步的一种机制。条件变量上的基本操作有:触发条件(当条件变为 true 时);等待条件,挂起线程直到其他线程触发条件。
必须和互斥锁进行配合使用
生产者消费者模型,生产者对带有头节点的链表头插方式push_front生产数据,消费者调用pop_front消费数据。而生产者可能动作比较慢,这时就会有问题。
生产者生产一个数据时间,消费者可能迫切需求。因此,一直轮寻申请锁资源,以便进行消费。所以就会产生多次不必的锁资源申请释放动作。影响系统性能。
信号量
参见进程:
一个线程创建和回收的实例
/*************************************************************************
> File Name: dm06_thread_attributes.c
> Author: ma6174
> Mail: ma6174@163.com
> Created Time: 2018年09月11日 星期二 19时43分03秒
************************************************************************/
#include<stdio.h>
#include<pthread.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
void *th_fun(void*arg)
{
int n = 20;
while(n--)
{
printf("%x %d\n",(int)pthread_self(),n);
sleep(1);
}
return (void*)1;
}
int main()
{
pthread_t tid;
pthread_attr_t attr;//attr is rubbish
int err;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
pthread_create(&tid,&attr,th_fun,NULL);
err = pthread_join(tid,NULL);
while(1)
{
if(err != 0){
printf("%s\n",strerror(err));
sleep(10);
pthread_exit((void*)1);
}
}
return 0;
}