进程 与 线程:
参考自: Linux多线程编程初探 - 峰子_仰望阳光 - 博客园 (cnblogs.com)
进程:
典型的UNIX/Linux进程可以看成只有一个控制线程:一个进程在同一时刻只做一件事情。
有了多个控制线程后,在程序设计时可以把进程设计成在同一时刻做不止一件事,每个线程各自处理独立的任务。进程是程序执行时的一个实例,是担当分配系统资源(CPU时间、内存等)的基本单位。
在面向线程设计的系统中,进程本身不是基本运行单位,而是线程的容器。
程序本身只是指令、数据及其组织形式的描述,进程才是程序(那些指令和数据)的真正运行实例。// 程序是静态概念,进程是动态概念
线程:
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。进程的所有信息对该进程的所有线程都是共享的,包括可执行的程序文本、程序的全局内存和堆内存、栈以及文件描述符。
二者联系:
进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。
线程有自己的堆栈和局部变量,但线程没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。
二者区别:
总的来说就是:进程有独立的地址空间,线程没有单独的地址空间(同一进程内的线程共享进程的地址空间)
使用线程理由:
1.和进程相比,它是一种非常 "节俭" 的多任务操作方式。省内存,切换效率高
2.线程间方便的通信机制 // 共享数据,方便线程间的通信 --(同一进程内的线程共享进程的地址空间)
"进程——资源分配的最小单位,线程——程序执行的最小单位"
Linux线程开发API:
多线程开发的最基本概念主要包含三点:线程,互斥锁,条件 3 + 4 + 5
线程操作又分线程的创建,退出,等待 3 种
互斥锁则包括 4 种操作,分别是创建,销毁,加锁和解锁。
条件操作有 5 种操作:创建,销毁,触发,广播和等待。
线程:
1. 线程创建
#include <pthread.h>
int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
// 返回:若成功返回0,否则返回错误编号
2. 线程退出
单个线程可以通过以下三种方式退出,在不终止整个进程的情况下停止它的控制流:
1)线程只是从启动例程中返回,返回值是线程的退出码。
2)线程可以被同一进程中的其他线程取消。
3)线程调用pthread_exit:
#include <pthread.h>
int pthread_exit(void *rval_ptr);
3. 线程等待
#include <pthread.h>
int pthread_join(pthread_t thread, void **rval_ptr);
// 返回:若成功返回0,否则返回错误编号
pthread_t pthread_self(void);
// 返回:调用线程的ID
case1:while来等待
#include <pthread.h>
#include <stdio.h>
// int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);void *func1(void *arg)
{//获取当前线程ID
printf("thread1: %ld pthread create.\n",(unsigned long)pthread_self());
printf("thread1: param is %d\n",*((int *)arg));
}int main()
{
pthread_t thread1;
int arg = 100;
int pret;//创建线程1-thread1,调用函数func1,传递参数arg,NULL 创建默认属性打的线程
pret = pthread_create(&thread1, NULL, func1, (void *)(&arg));
if(!pret){//进程创建成功返回0
puts("create success.");
}
else {//失败返回-1
puts("create failed.");
}
printf("main: %ld .\n",(unsigned long)pthread_self());// 获取main函数的线程ID
while(1); // 不加的话,主线程会没创建完线程就退出return 0;
}
========================================
case2:结合等待退出的API来调用:
#include <pthread.h>
#include <stdio.h>
// int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
// int pthread_exit(void *rval_ptr);
// int pthread_join(pthread_t thread, void **rval_ptr);
void *func1(void *arg)
{
//static int pret=88; //static 保护pret退出这个函数不被销毁
static char * p="this is run out."; // 返回字符串
printf("thread1: %ld pthread create.\n",(unsigned long)pthread_self());
printf("thread1: param is %d\n",*((int *)arg));
// pthread_exit((void*)&pret);
// 线程退出 -- 里面的参数 -- 线程退出状态 -- 自己设计,可以是int 也可以是char*
pthread_exit((void*)p); // 返回字符串
}int main()
{
pthread_t thread1;
// int* pthr=NULL;
char* pthr=NULL; // 接收返回的字符串
int arg = 100;
int pret;
pret = pthread_create(&thread1, NULL, func1, (void *)(&arg)); //arg --参数传递
if(!pret){
puts("create success.");
}
else {
puts("create failed.");
}
printf("main: %ld .\n",(unsigned long)pthread_self());
// 等待线程thread1退出(线程没退出phread_exit就会阻塞),第二个参数收回线程的退出状态,NULL--不收回
pthread_join(thread1,(void**)&pthr); // 指针再取地址达到二级指针类型才能强制装换位二级指针
//printf("thread quit id:%d\n",*pthr);// 查看指针拿到的退出数据
printf("thread quit id:%s\n",pthr);// 输出进程退出返回值,字符串名就是指针
return 0;
}
========================================
case3: 线程共享内存验证:
#include <pthread.h>
#include <stdio.h>
#include<unistd.h>// int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
int g_data=0;// 共享变量
void *func1(void *arg)
{
printf("thread1: %ld pthread create.\n", (unsigned long)pthread_self());
printf("thread1: param is %d\n", *((int *)arg));
while(1){
printf("thread1: %d\n",g_data++);
sleep(1);
}
// pthread_exit(NULL);
}
void *func2(void *arg)
{
printf("thread2: %ld pthread create.\n", (unsigned long)pthread_self());
printf("thread2: param is %d\n", *((int *)arg));
while(1){
printf("thread2: %d\n",g_data++);
sleep(1);
}
// pthread_exit(NULL);
}int main()
{
pthread_t thread1;
pthread_t thread2;
int arg = 100;
int pret;
pret = pthread_create(&thread1, NULL, func1, (void *)(&arg));
if (!pret)
{
puts("main:create thread1 success.");
}
else
{
puts("main:create thread1 failed.");
}pret = pthread_create(&thread2, NULL, func2, (void *)(&arg));
if (!pret)
{
puts("main:create thread2 success.");
}
else
{
puts("main:create thread2 failed.");
}
while(1){
printf("main: %d\n",g_data++);
sleep(1);
}
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
printf("main: %ld .\n", (unsigned long)pthread_self());
;return 0;
}
==============================输出结果中,我们发现在func1,func2,main函数三个线程中,g_data的数据时不重样且递增的,什么我们的线程共享了进程分配到的内存
互斥锁API的使用:
概念:
互斥量(mutex)从本质上来说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁
1. 创建及销毁互斥锁
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t mutex);
// 返回:若成功返回0,否则返回错误编号
要用默认的属性初始化互斥量,只需把attr设置为NULL。
2. 加锁及解锁
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t mutex);
int pthread_mutex_trylock(pthread_mutex_t mutex);
int pthread_mutex_unlock(pthread_mutex_t mutex);
// 返回:若成功返回0,否则返回错误编号
上锁后,只有在锁里面的代码执行完后才解锁,解锁前不会去调用其他进程
互斥量就是一把锁,对锁有上锁和解锁操作,这两个操作之间的代码叫共享资源,没执行完共享资源进程前,
其他进程不能调用,执行完后,其他进程可以去争抢这把锁,第一个争抢到的进程又能上锁,解锁, 这样就保证了
我们的进程是一个线程一个线程的执行,在没有执行完单曲线程之前不会去执行其他线程
case1: 互斥锁应用
#include <pthread.h>
#include <stdio.h>
#include<unistd.h>// int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
int g_data=0;// 共享变量
pthread_mutex_t mutex;
void *func1(void *arg)
{
int i;// 上锁 -- 然后后续想要再上这把锁mutex 就会被阻塞,直到这把锁被解锁,保证了上锁解锁里面的函数快能执行完,才去执行其他进程,保证进程执行的时候不受打扰
pthread_mutex_lock(&mutex);
for(i=0;i<3;++i)
{
printf("thread1: %ld pthread create.\n", (unsigned long)pthread_self());
printf("thread1: param is %d\n", *((int *)arg));
sleep(1);
}//解锁
pthread_mutex_unlock(&mutex);
// pthread_exit(NULL);
}
void *func2(void *arg)
{
pthread_mutex_lock(&mutex); // 进程1没开锁你上不了锁,阻塞在这里
printf("thread2: %ld pthread create.\n", (unsigned long)pthread_self());
printf("thread2: param is %d\n", *((int *)arg));
pthread_mutex_unlock(&mutex);
// pthread_exit(NULL);
}int main()
{
pthread_t thread1;
pthread_t thread2;
int arg = 100;
int pret;
//int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
// 初始化我们的互斥🔓
pthread_mutex_init(&mutex,NULL);
pret = pthread_create(&thread1, NULL, func1, (void *)(&arg));
if (!pret)
{
puts("main:create thread1 success.");
}
else
{
puts("main:create thread1 failed.");
}pret = pthread_create(&thread2, NULL, func2, (void *)(&arg));
if (!pret)
{
puts("main:create thread2 success.");
}
else
{
puts("main:create thread2 failed.");
}pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
printf("main: %ld .\n", (unsigned long)pthread_self());
pthread_mutex_destroy(&mutex);//销毁互斥锁
while (1);
return 0;
}
==================================
case2: 互斥锁 实现对共享资源的访问:
让g_data在thread1中加到3在退出,or去执行其他进程,main函数里面没上锁就能访问
进程是线程的容器,线程可以控制进程的退出
#include <pthread.h>
#include <stdio.h>
#include<unistd.h>
#include<stdlib.h>// int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
int g_data=0;// 共享变量
pthread_mutex_t mutex;
void *func1(void *arg)
{printf("thread1: %ld pthread create.\n", (unsigned long)pthread_self());
printf("thread1: param is %d\n", *((int *)arg));
pthread_mutex_lock(&mutex);//先让下面循环里的代码执行完,再解锁
for(;;)
{
printf("thread1:%d\n",g_data++);
sleep(1);
if(g_data==3){
pthread_mutex_unlock(&mutex); //用完解锁
puts("------------thread1 quit-----------------------");
//pthread_exit(NULL);
exit(0);
}}
// pthread_exit(NULL);
}
void *func2(void *arg)
{
printf("thread2: %ld pthread create.\n", (unsigned long)pthread_self());
printf("thread2: param is %d\n", *((int *)arg));
for(;;)
{
printf("thread2:%d\n",g_data);
pthread_mutex_lock(&mutex); // 由于进程1没有解锁,他就会阻塞再这里上不了锁
g_data++;
pthread_mutex_unlock(&mutex);
sleep(1);
}
// pthread_exit(NULL);
}int main()
{
pthread_t thread1;
pthread_t thread2;
int arg = 100;
int pret;
//int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
// 初始化🔓
pthread_mutex_init(&mutex,NULL);
pret = pthread_create(&thread1, NULL, func1, (void *)(&arg));
if (!pret)
{
puts("main:create thread1 success.");
}
else
{
puts("main:create thread1 failed.");
}pret = pthread_create(&thread2, NULL, func2, (void *)(&arg));
if (!pret)
{
puts("main:create thread2 success.");
}
else
{
puts("main:create thread2 failed.");
}
printf("main: %ld .\n", (unsigned long)pthread_self());
while(1){
printf("main: g_data=%d\n",g_data);
sleep(1);}
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
pthread_mutex_destroy(&mutex); //线程全部退出后销毁🔓
return 0;
}
==========================
配置测试文件测试;
1).sh文件 -- shell 脚本测试
vi test.sh
//添加3个./a.out
chmod -x test.sh
// 给test可执行的权限
./test --执行三次./a.out 来测试
2).c文件使用system测试
int main()
{
int k=3;
while(k--){
system("./a.out");}
return 0;
}
=========================================
死锁
什么情况下回死锁:
pthread_mutex_t mutex;
pthread_mutex_t mutex2;
void *func1(void *arg)
{
pthread_mutex_lock(&mutex);
sleep(3);
pthread_mutex_lock(&mutex2);int i;
for(i=0;i<3;++i)
{
printf("thread1: %ld pthread create.\n", (unsigned long)pthread_self());
printf("thread1: param is %d\n", *((int *)arg));
sleep(1);
}
pthread_mutex_unlock(&mutex);
// pthread_exit(NULL);
}
void *func2(void *arg)
{
pthread_mutex_lock(&mutex2);
sleep(3);
pthread_mutex_lock(&mutex);printf("thread2: %ld pthread create.\n", (unsigned long)pthread_self());
printf("thread2: param is %d\n", *((int *)arg));
pthread_mutex_unlock(&mutex);
// pthread_exit(NULL);
}
前提2个锁:
具体:
上面这种情况就会造成死锁,进程1拿到锁1,sleep(3),期间进程2拿到锁2,sleep(3),进程1一直拿不到锁2被进程2拿走
造成了阻塞,同理,之后进程2也拿不到锁1, 这就成为了死锁
通用:
两个线程都拿到一个锁,而且都不解锁,但是都想去拿另一个锁(拿不到,被另一个线程拿走了),导致一直阻塞,造成死锁
=========================================
条件API
1. 创建及销毁条件变量#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t cond);
// 返回:若成功返回0,否则返回错误编号
除非需要创建一个非默认属性的条件变量,否则pthread_cont_init函数的attr参数可以设置为NULL。2. 等待
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, cond struct timespec *restrict timeout);
// 返回:若成功返回0,否则返回错误编号
pthread_cond_wait等待条件变为真。如果在给定的时间内条件不能满足,那么会生成一个代表一个出错码的返回变量。传递给pthread_cond_wait的互斥量对条件进行保护,调用者把锁住的互斥量传给函数。函数把调用线程放到等待条件的线程列表上,然后对互斥量解锁,这两个操作都是原子操作。这样就关闭了条件检查和线程进入休眠状态等待条件改变这两个操作之间的时间通道,这样线程就不会错过条件的任何变化。pthread_cond_wait返回时,互斥量再次被锁住。pthread_cond_timedwait函数的工作方式与pthread_cond_wait函数类似,只是多了一个timeout。timeout指定了等待的时间,它是通过timespec结构指定。
3. 触发
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t cond);
int pthread_cond_broadcast(pthread_cond_t cond);
// 返回:若成功返回0,否则返回错误编号
这两个函数可以用于通知线程条件已经满足。pthread_cond_signal函数将唤醒等待该条件的某个线程,而pthread_cond_broadcast函数将唤醒等待该条件的所有进程
线程控制实现线程的同步:
case:通过条件控制g_data在进程2加到3再到进程1操作一次恢复,重新让进程2操作
#include <pthread.h>
#include <stdio.h>
#include<unistd.h>
#include<stdlib.h>// int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
int g_data=0;// 共享变量
pthread_mutex_t mutex;
pthread_cond_t cond;// 定义条件变量void *func1(void *arg)
{printf("thread1: %ld pthread create.\n", (unsigned long)pthread_self());
printf("thread1: param is %d\n", *((int *)arg));
for(;;)
{
pthread_cond_wait(&cond,&mutex);//等待条件发生 -- signal触发
puts("------------thread1-----------------------");
//pthread_exit(NULL);
printf("thread1:%d\n",g_data++);
g_data=0;
sleep(1);}
// pthread_exit(NULL);
}
void *func2(void *arg)
{
printf("thread2: %ld pthread create.\n", (unsigned long)pthread_self());
printf("thread2: param is %d\n", *((int *)arg));
for(;;)
{
printf("thread2:%d\n",g_data);
pthread_mutex_lock(&mutex); // 上锁,保证,上锁解锁间的代码块能完整执行
g_data++;
if(g_data==3){
pthread_cond_signal(&cond);//单次唤醒wait,这时候func里面的wait触发就会往下执行}
pthread_mutex_unlock(&mutex);
sleep(1);
}
// pthread_exit(NULL);
}int main()
{
pthread_t thread1;
pthread_t thread2;
int arg = 100;
int pret;
//int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
pthread_cond_init(&cond,NULL); //创造条件变量
// 初始化🔓
pthread_mutex_init(&mutex,NULL);
pret = pthread_create(&thread1, NULL, func1, (void *)(&arg));
if (!pret)
{
// puts("main:create thread1 success.");
}
else
{
puts("main:create thread1 failed.");
}pret = pthread_create(&thread2, NULL, func2, (void *)(&arg));
if (!pret)
{
// puts("main:create thread2 success.");
}
else
{
puts("main:create thread2 failed.");
}
printf("main: %ld .\n", (unsigned long)pthread_self());
while(1){
// printf("main: g_data=%d\n",g_data);
sleep(1);}
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
pthread_cond_destroy(&cond);// 销毁条件变量
pthread_mutex_destroy(&mutex); //线程全部退出后销毁🔓
return 0;
}
================================
测试常用: 在指令执行后 & --后台运行。比如 ./a.out &
====================================
case2:使用宏来静态初始化:
#include <pthread.h>
#include <stdio.h>
#include<unistd.h>
#include<stdlib.h>// int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
int g_data=0;// 共享变量
//使用宏来静态初始化
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;void *func1(void *arg)
{
static int cnt=0;
printf("thread1: %ld pthread create.\n", (unsigned long)pthread_self());
printf("thread1: param is %d\n", *((int *)arg));
for(;;)
{
pthread_cond_wait(&cond,&mutex);//等待条件发生 -- signal触发
puts("------------thread1-----------------------");
//pthread_exit(NULL);
printf("thread1:%d\n",g_data);
g_data=0;
sleep(1);
if(cnt++ ==10){
exit(1);
}}
// pthread_exit(NULL);
}
void *func2(void *arg)
{
printf("thread2: %ld pthread create.\n", (unsigned long)pthread_self());
printf("thread2: param is %d\n", *((int *)arg));
for(;;)
{
printf("thread2:%d\n",g_data);
pthread_mutex_lock(&mutex);
g_data++;
if(g_data==3){
pthread_cond_signal(&cond);//单次唤醒wait}
pthread_mutex_unlock(&mutex);
sleep(1);
}
// pthread_exit(NULL);
}int main()
{
pthread_t thread1;
pthread_t thread2;
int arg = 100;
int pret;
//动态初始化 ,对应宏是静态初始化
//int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
// pthread_cond_init(&cond,NULL); //创造条件变量
// 初始化🔓
// pthread_mutex_init(&mutex,NULL);
pret = pthread_create(&thread1, NULL, func1, (void *)(&arg));
if (!pret)
{
// puts("main:create thread1 success.");
}
else
{
puts("main:create thread1 failed.");
}pret = pthread_create(&thread2, NULL, func2, (void *)(&arg));
if (!pret)
{
// puts("main:create thread2 success.");
}
else
{
puts("main:create thread2 failed.");
}
printf("main: %ld .\n", (unsigned long)pthread_self());
while(1){
// printf("main: g_data=%d\n",g_data);
sleep(1);}
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
pthread_cond_destroy(&cond);// 销毁条件变量
pthread_mutex_destroy(&mutex); //线程全部退出后销毁🔓
return 0;
}