进程:程序是人使用计算机语言编写的,可以实现一定功能,并且可以执行的代码集合。而进程是正在执行中的程序。当程序被执行时,执行人的权限和属性,以及程序的代码都会被加载入内存,操作系统给这个进程分配一个 ID,称为 PID(进程 ID)。是资源分配的基本单位。
线程:线程是进程中的一个实体,是进程的一部分,一个没有线程的进程可以被看作是单线程的。线程有时又被称为轻权进程或轻量级进程,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。是 CPU 调度的一个基本单位。
多线程:多线程就是指一个进程中同时有多个执行路径(线程)正在执行。在一个程序中,有很多的操作是非常耗时的,如数据库读写操作,IO操作等,如果使用单线程,那么程序就必须等待这些操作执行完成之后才能执行其他操作。使用多线程,可以在将耗时任务放在后台继续执行的同时,同时执行其他操作。
一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。
那么在使用多线程的时候就得注意线程安全。线程安全是多线程编程时的计算机程序代码中的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。
互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
多线程就好比如你妈妈有三个孩子,这三个孩子就是三个线程,这时候你妈妈买回来一个玩具给三个孩子玩,这个玩具是三个孩子共有的,这三个孩子都想玩这个玩具,一起共同去争抢这个玩具,最后抢着抢着玩具就坏了。这个玩具就相当于程序中的全局变量,一个线程想用这个全局变量,另外一个线程也想用这个变量,共同争抢一个资源,很容易破坏程序的一致性。想要保护这个玩具,这时候就得用到互斥锁,互斥锁作用就是在同一时候这个玩具只能一个孩子玩。即一个孩子拿着这个玩具关上门,上锁,另外领个孩子就不能接触到,只能等待这个孩子解锁开门,轮到下一个孩子玩。这就保证了程序的一致性。
当然互斥你也可以理解为在学校宿舍6人间只有意见卫生间,当某个舍友进去,会把门锁上,当完成他想做的事,会把门锁打开,在让下一个舍友进去。这6人就代表6个线程,卫生间代表着程序的全局变量。
以下程序是线程的创建,传参,互斥:
#include <stdio.h>
#include <pthread.h>
#include <string.h>
typedef struct Meter {
char name[128];
int age;
}Meter_t;
pthread_mutex_t mutex_lock; //定义一个互斥全局名
int count = 0;
void* thread_test(void *argv)
{
Meter_t inmet;
memcpy(&inmet, (Meter_t *)argv, sizeof(Meter_t));
//pthread_detach(pthread_self()); //如果线程中有循环,可以保证主线程不会阻塞,保证内存不会泄露
pthread_mutex_lock(&mutex_lock); //加锁
count = ++count; //加锁后只允许一个线程访问,只有解锁后下一个线程才能继续访问
pthread_mutex_unlock(&mutex_lock); //解锁
printf("name: %s\n", inmet.name);
printf("age: %d\n", inmet.age);
}
int main(int argc, char const *argv[])
{
pthread_mutex_init(&mutex_lock,NULL); //互斥锁的初始化
pthread_t id1,id2;
Meter_t stra1;
strcpy(stra1.name, "zhangsan");
stra1.age = 18;
pthread_create(&id1,NULL,thread_test,(void *)(&stra1)); //创建线程,并传入参数
Meter_t stra2;
strcpy(stra2.name, "lisi");
stra2.age = 19;
pthread_create(&id2,NULL,thread_test,(void *)(&stra2));//创建线程,并传入参数
pthread_join(id1,NULL); //等待id1线程执行的结束
pthread_join(id2,NULL); //等待id2线程执行的结束
printf("count: %d\n",count);
return 0;
}
程序的结果如下:
同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。
同步的代码如下:一个线程减10,但是不会到负数,当count=0的时候,会等待另一个线程加10,另一个线程在发信息说对这个线程说我已经加10了,你现在可以减10了。意味着程序都会让加10的线程先运行完成,减10的线程才能运行完成,从而达到线程有序的访问。
#include <stdio.h>
#include <pthread.h>
#include <string.h>
typedef struct Meter {
char name[128];
int age;
}Meter_t;
pthread_mutex_t mutex_lock; //互斥
pthread_cond_t count_event; //同步
int count = 0;
void* thread_test1(void *argv)
{
pthread_mutex_lock(&mutex_lock); //加锁,加锁后只允许一个线程访问,只有解锁后下一个线程才能继续访问
Meter_t inmet;
memcpy(&inmet, (Meter_t *)argv, sizeof(Meter_t));
//pthread_detach(pthread_self()); //如果线程中有循环,可以保证主线程不会阻塞,保证内存不会泄露
while(count == 0 ){
pthread_cond_wait(&count_event,&mutex_lock); //等待事件的发生,并解锁之后再加锁
}
count = count-10;
printf("test1 count: %d\n",count);
pthread_mutex_unlock(&mutex_lock); //解锁
}
void* thread_test2(void *argv)
{
pthread_mutex_lock(&mutex_lock); //加锁,加锁后只允许一个线程访问,只有解锁后下一个线程才能继续访问
Meter_t inmet;
memcpy(&inmet, (Meter_t *)argv, sizeof(Meter_t));
//pthread_detach(pthread_self()); //如果线程中有循环,可以保证主线程不会阻塞,保证内存不会泄露
count = count+10;
if(count-10 == 0 ){
pthread_cond_signal(&count_event); //通知事件的发生
}
printf("test2 count: %d\n",count);
pthread_mutex_unlock(&mutex_lock); //解锁
}
int main(int argc, char const *argv[])
{
pthread_mutex_init(&mutex_lock,NULL); //互斥锁的初始化
pthread_cond_init(&count_event,NULL); //同步的初始化
pthread_t id1,id2;
Meter_t stra1;
strcpy(stra1.name, "zhangsan");
stra1.age = 18;
pthread_create(&id1,NULL,thread_test1,(void *)(&stra1)); //创建线程,并传入参数
Meter_t stra2;
strcpy(stra2.name, "lisi");
stra2.age = 19;
pthread_create(&id2,NULL,thread_test2,(void *)(&stra2));//创建线程,并传入参数
pthread_join(id1,NULL); //等待id1线程执行的结束
pthread_join(id2,NULL); //等待id2线程执行的结束
printf("count: %d\n",count);
return 0;
}
程序运行结果如下: