进程和线程
进程:一个正在运行的程序。
状态:就绪,运行,阻塞;
线程是进程中的一个执行路径,一个进程中至少有一个主线程(main函数);
有多条执行路径为多线程。
线程种类
三种线程
纯粹的用户级(并发)、存粹的内核级(并行)、组合(可用多处理器的资源,用户空间也可创建多个线程)。
操作系统用内核级
Linux把所有线程当作进程来实现、线程仅仅被视为一个与其他进程共享某些资源的进程(如地址空间),每个线程都有一个自己的PCB(进程控制块,task_struct);
创建一个线程
用pthread_create()创建
记得编译的时候后面加-pthread
函数pthread_join用来等待一个线程的结束,线程间同步的操作。
多个线程用同一个进程的地址空间
#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”);
sleep(1);
}
pthread_exit(“fun over”);//可以返回线程信息给主线程
}
int main()
{
pthread_t id;
//create执行一次创建一个进程
pthread_create(&id,NULL,fun,NULL);//创建了一个fun线程
for(int i=0;i<5;i++)
{
printf(“main run\n”);
sleep(1);
}
char *s = NULL;
pthread_join(id,(void**)&s);
//等待线程返回的信息,如果子线程没有运行完,主线程会被阻塞,可以防止fun线程没有运行完main就结束。
printf(“s=%s\n”,s);//不能用临时变量
exit(0);
}
两个路径同时进行,主函数(主进程)退出,其余进程也会结束,尽量让主函数慢点退出,sleep一下。
当然,用了pthread_join();接收返回信息阻塞可以不用sleep
了解程序的并发运行
并行是特殊的并发,我的虚拟机有4个处理器,所以准确来说这几个线程应该是并行的。
同时创建多个线程并打印自己是第几个线程:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
void *fun(void *arg)//这是一个线程
{
int index = *(int*)arg;
printf(“index=%d\n”,index);
sleep(1);
printf(“index=%d\n”,index);
pthread_exit(NULL);//可以返回线程信息给主线程
}
int main()
{
pthread_t id[5];
//create执行一次创建一个进程
int i=0;
for(i=0;i<5;i++)
{
pthread_create(&id[i],NULL,fun,(void*)&i);//创建线程,传地址
}
for(i=0;i<5;i++)
{
pthread_join(id[i],NULL);
}
exit(0);
}
运行结果:
程序运行有不确定性(少用多线程)
因为线程获取i是靠i的地址,可能第一次循环的时候这些线程还没来得及获取i的值,到第二个for循环i被置0时才取得i的值,所以线程输出全是0。
获取i的值的速度差不多(同时获取值出现几个相同的),写入缓冲区的速度不一样不一定从0到4。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
int g_count = 1;
void *fun(void *arg)//这是一个线程
{
for(int i=0;i<1000;i++)
{
printf(“g_count=%d\n”,g_count++);
}
pthread_exit(NULL);//可以返回线程信息给主线程
}
int main()
{
pthread_t id[5];
//create执行一次创建一个进程
for(int i=0;i<5;i++)
{
pthread_create(&id[i],NULL,fun,NULL);//创建线程,传地址
}
for(int i=0;i<5;i++)
{
pthread_join(id[i],NULL);
}
exit(0);
}
理想的结果应该是5000,每个线程加1000次,但是结果会出现4999,4998等,这是因为两个进程同时执行了++,然后把数值存回去,本来应加两次变成了加一次,导致结果变小了。
线程同步
控制程序的执行速度或流程,保证程序的正确性。
线程同步方法:(线程间)信号量、互斥锁、条件变量、读写锁
用同步解决上面结果<5000的问题:
信号量:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
#include<semaphore.h>//信号量引用
int g_count = 1;
sem_t sem;//信号量变量
void *fun(void *arg)
{
for(int i=0;i<1000;i++)
{
sem_wait(&sem);//p
printf(“g_count=%d\n”,g_count++);
sem_post(&sem);//v
}
pthread_exit(NULL);
}
int main()
{
sem_init(&sem,0,1);//初始化sem=1
pthread_t id[5];
for(int i=0;i<5;i++)
{
pthread_create(&id[i],NULL,fun,NULL);//创建线程,传地址
}
for(int i=0;i<5;i++)
{
pthread_join(id[i],NULL);
}
sem_destory(&sem);//销毁
exit(0);
}
互斥锁:
互斥锁:pthread_mutex_t
加锁的程序性能会下降
自己用时先加锁,此时别人用不了(别人无法加锁:线程被阻塞),自己用完再解锁。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
#include<semaphore.h>//互斥锁引用
int g_count = 1;
pthread_mutex_t mutex;//互斥锁
void *fun(void *arg)
{
for(int i=0;i<1000;i++)
{
pthread_mutex_lock(&mutex);//加锁
printf(“g_count=%d\n”,g_count++);
pthread_mutex_unlock(&mutex);//解锁
}
pthread_exit(NULL);
}
int main()
{
pthread_mutex_init(&mutex,NULL);//初始化
pthread_t id[5];
for(int i=0;i<5;i++)
{
pthread_create(&id[i],NULL,fun,NULL);
}
for(int i=0;i<5;i++)
{
pthread_join(id[i],NULL);
}
pthread_mutex_destroy(&mutex);//销毁锁
exit(0);
}
读写锁:
读写锁数据类型pthread_rwlock_t
接口:
#include <pthread.h>
int pthread_rwlock_init(pthread_rwlock_t *rwlock,pthread_rwlockattr_t *attr);
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t*rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
pthread_rwlock_t lock;
void* thread_w_fun(void*arg)
{
for(int i=0;i<10;i++)
{
pthread_rwlock_wrlock(&lock);
printf("-----------thread-w-start\n");
sleep(3);
printf("-----------thread-w-end\n");
pthread_rwlock_unlock(&lock);
sleep(1);
}
}
void*thread_r_fun1(void*arg)
{
for(int i=0;i<10;i++)
{
pthread_rwlock_rdlock(&lock);
printf("fun1 r start\n");
sleep(1);
printf("fun1 r end\n");
pthread_rwlock_unlock(&lock);
sleep(1);
}
}
void*thread_r_fun2(void*arg)
{
for(int i=0;i<10;i++)
{
pthread_rwlock_rdlock(&lock);
printf("fun2 r start\n");
sleep(2);
printf("fun2 r end\n");
pthread_rwlock_unlock(&lock);
sleep(1);
}
}
int main()
{
pthread_rwlock_init(&lock,NULL);//初始化
pthread_t id1,id2,id3;//创建三个id接收读写锁的线程id
pthread_create(&id1,NULL,thread_r_fun1,NULL);//创建读锁1线程
pthread_create(&id2,NULL,thread_r_fun2,NULL);//读锁2
pthread_create(&id3,NULL,thread_w_fun,NULL);//写锁
pthread_join(id1,NULL);
pthread_join(id2,NULL);
pthread_join(id3,NULL);
pthread_relock_destroy(&lock);//销毁锁
exit(0);
}
多个读(或多个写)可以同时运行,一旦开始写就不能读,同理,一旦读就不能写了。
条件变量
条件变量:
条件变量提供了一种线程间的通知机制:当某个共享数据达到某个值的时候,唤醒等待
这个共享数据的线程。
使用头文件:#include <pthread.h>
//初始化:
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *attr);
//阻塞,把线程放条件变量队列中
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
//唤醒单个线程
int pthread_cond_signal(pthread_cond_t *cond);
//唤醒所有等待的线程
int pthread_cond_broadcast(pthread_cond_t *cond);
//销毁
int pthread_cond_destroy(pthread_cond_t *cond);
注意:阻塞的时候必须要在前面加锁,后面解锁。
使用示例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
char buff[128] = {0};
pthread_mutex_t mutex;//互斥锁
pthread_cond_t cond;//条件变量
//线程a
void*funa(void* arg)
{
printf("aa start\n");
while(1)
{
pthread_mutex_lock(&mutex);//加锁
pthread_cond_wait(&cond,&mutex);//阻塞,把线程放条件变量队列中
pthread_mutex_unlock(&mutex);//解锁
if(strncmp(buff,"end",3)==0)//如果输入的是end,结束线程
{
break;
}
printf("funa:%s",buff);
}
printf("funa over\n");
}
//线程b
void*funb(void* arg)
{
printf("bb start\n");
while(1)
{
pthread_mutex_lock(&mutex);//加锁
pthread_cond_wait(&cond,&mutex);//阻塞,(解锁,加锁)
pthread_mutex_unlock(&mutex);//解锁
if(strncmp(buff,"end",3)==0)
{
break;
}
printf("funb:%s",buff);
}
//唤醒时不希望有内容进出
printf("funb over\n");
}
//线程c
void*func(void* arg)
{
printf("cc start\n");
while(1)
{
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond,&mutex);//阻塞,把线程放条件变量队列中
pthread_mutex_unlock(&mutex);
if(strncmp(buff,"end",3)==0)
{
break;
}
printf("func:%s",buff);
}
printf("func over\n");
}
//主函数
int main()
{
//初始化
pthread_mutex_init(&mutex,NULL);//互斥锁
pthread_cond_init(&cond,NULL);//条件变量
//创建线程
pthread_t id1,id2,id3;
pthread_create(&id1,NULL,funa,NULL);
pthread_create(&id2,NULL,funb,NULL);
pthread_create(&id3,NULL,func,NULL);
//同步数据
while(1)
{
char tmp[128] = {0};
fgets(tmp,128,stdin);
strcpy(buff,tmp);//写入数据
if(strncmp(tmp,"end",3)==0)//如果输入end,唤醒所有线程,使其结束
{//唤醒所有线程
pthread_cond_broadcast(&cond);
break;
}
else//否则唤醒任意线程,同步数据
{//唤醒任意线程
pthread_cond_signal(&cond);
}
}
//等待线程结束
pthread_join(id1,NULL);
pthread_join(id2,NULL);
pthread_join(id3,NULL);
//结束
exit(0);
}