一、简单认知
进程:一个正在运行的程序
线程:进程内部的一个执行路径
头文件:#include<pthread.h>
二、进程与线程的区别
三、线程的实现方式
用户级:开销小,可以创建很多,但不能利用多处理器资源。就算你弄了多了线程,但在内核中你只有一个线程,尽管你有多个处理器,还是通过时间片轮转法来实现并发
内核级:开销大(相对用户级来说),由内核直接管理,可以利用多处理器资源。Linux采用的就是这种
组合:介于这三种之间
在操作系统中,线程的实现有以下三种方式:
◼ 用户级线程 :用户级线程是完全在用户空间中实现的线程。操作系统内核对其一无所知,只知道进程的存在。用户级线程的创建、调度和管理完全由用户级的线程库完成。由于这些操作不需要内核的介入,所以用户级线程的创建和切换比内核级线程更加快速、高效。然而,由于操作系统对用户级线程一无所知,因此一个阻塞的用户级线程会导致整个进程阻塞,这是用户级线程的一个主要缺点。
◼ 内核级线程 :内核级线程是直接由操作系统内核支持和管理的线程。内核维护了所有内核线程的上下文信息,并负责线程的调度和切换。因此,内核级线程可以利用多处理器并行性,同时,当一个内核级线程阻塞时,内核可以调度该进程的其他线程执行。然而,内核级线程的创建和切换需要进行用户态到内核态的切换,因此成本比用户级线程高。
◼ 组合级线程:组合级线程是用户级线程和内核级线程的组合,试图结合两者的优点。在这种模型中,一个用户级线程对应于一个或多个内核级线程。这样,即使一个用户级线程阻塞,也不会阻塞整个进程,因为内核可以调度对应的其他内核级线程执行。同时,用户级线程的创建和切换可以在用户空间内完成,避免了频繁的用户态到内核态的切换。
四、一些栗子
1、第一个小栗子
step1:不加睡眠函数
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
void* fun(void* arg){
for(int i=0;i<5;i++)
{
printf("fun run\n");
}
}
int main()
{
pthread_t id;
pthread_create(&id,NULL,fun,NULL);
for(int i=0;i<5;i++)
{
printf("main run\n");
//sleep(1);
}
exit(0);
}
结果:
一般是全是主函数,因为在调用线程函数的时候主函数已经执行完毕,Exit(0)了,所以不会打印fun函数
step 2:主函数加上睡眠函数
void* fun(void* arg){
for(int i=0;i<5;i++)
{
printf("fun run\n");
}
}
int main()
{
pthread_t id;
pthread_create(&id,NULL,fun,NULL);
for(int i=0;i<5;i++)
{
printf("main run\n");
sleep(1);
}
exit(0);
}
2、第二个小栗子
循环打印所在位置
void*thread_fun(void*arg)
{
int*p=(int*)arg;
int index=*p;
for(int i=0;i<3;i++)
{
printf("intdex=%d\n",index);
sleep(1);
}
}
int main()
{
pthread_t id[5];
for(int i=0;i<5;i++)
{
pthread_create(&id[i],NULL,thread_fun,(void*)&i);
}
for(int i=0;i<5;i++){
pthread_join(&id[i],NULL);
}
}
结果
问题:
这里将i的地址传给了fun()参数,解引用后就能得到i的值,这在单线程(一个内核)是没得问题的,但是多核多线程是有的,每个线程同时获取i的地址,但是线程启用是需要花费时间,等打印获取i的值时,那一刻的i不一定是你传入时期的i.
3.第三个小例子
将一个全局变量加到5000
#include<pthread.h>
int g_val=1;
void*fun(void* arg)
{
for(int i=0;i<1000;i++)
{
printf("val=%d\n",g_val++);
}
}
int main()
{
pthread_t id[5];
int i=0;
for(;i<5;i++)
{
pthread_create(&id[i],NULL,fun,NULL);
}
for(i=0;i<5;i++)
{
pthread_join(id[i],NULL);
}
exit(0);
}
结果不到5000,如果只有一个处理器的话不会出现两个程序并行的情况,但是处理器大于2时,出现两个程序并行。
解决方案:
1.加信号量
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
#include<semaphore.h>
int g_val=1;
sem_t sem;
void*fun(void* arg)
{
for(int i=0;i<1000;i++)
{
sem_wait(&sem);
printf("val=%d\n",g_val++);
sem_post(&sem);
}
}
int main()
{
pthread_t id[5];
sem_init(&sem,0,1);
int i=0;
for(;i<5;i++)
{
pthread_create(&id[i],NULL,fun,NULL);
}
for(i=0;i<5;i++)
{
pthread_join(id[i],NULL);
}
sem_destroy(&sem);
exit(0);
}
2.加互斥锁
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
#include<semaphore.h>
int g_val=1;
pthread_mutex_t mutex;
void*fun(void* arg)
{
for(int i=0;i<1000;i++)
{
pthread_mutex_lock(&mutex);
printf("val=%d\n",g_val++);
pthread_mutex_unlock(&mutex);
}
}
int main()
{
pthread_t id[5];
pthread_mutex_init(&mutex,NULL);
int i=0;
for(;i<5;i++)
{
pthread_create(&id[i],NULL,fun,NULL);
}
for(i=0;i<5;i++)
{
pthread_join(id[i],NULL);
}
pthread_mutex_destroy(&mutex);
exit(0);
}
留一下一道思考题轮流打印abcabc按照这个顺序,该如何处理