摘要:线程是如何创建的,线程之间的通信是如何做到的,线程之间通信需要注意什么,线程的同步与互斥是如何使用临界资源的,今天,又是我们一起努力学习的一天,一起来看看。

什么是线程,昨天我们学习了进程,说到每个进程的地址空间都是相互独立的,每个进程都有一个stask_struck。在进行进程切换时,需要不断地刷新cache缓存,比较消耗资源,为了减少这种消耗,就引入了轻量级的进程----线程。

线程的特点:同一个进程创建的多个线程共用同一个进程的地址空间。当进程创建线程后,原本的进程也叫做线程,成为主线程。

那么线程又是如何创建的?我们先来看一下创建线程的函数:

#include <pthread.h>int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start_routine)(void *),void *arg);

乍一看,这个函数如此复杂,竟然有四个参数。没关系,我们一个一个的来看。

① thread 这个参数代表着线程对象,每个线程对应一个线程对象。

在使用之前得先定义出来,看它类型,可以看出是pthread_t 类型的。

② attr 这是线程属性,默认填缺省属性NULL。

③ void *(*start_routine)(void *)   这个东西看起来复杂,其实就是填函数名,线程用一个函数去封装,所以填的这个函数名就是我们线程的入口。

④  arg 这个是填需要往线程里传递的参数。一次只能传递一个参数,想要传递多个参数的话,可以先定义一个结构体,传递结构体名,不想传递参数就填NULL。

从函数中可以看出返回值是int 类型的,创建线程成功返回0,失败则返回1,可用于判断线程是否创建成功。

和进程一样,有创建自然就有结束线程和等待线程。先来看结束线程的函数:

#include <pthread.h>void pthread_exit(void *reatval);

就reatval 一个参数,这个参数填的是线程结束时返回的信息,由等待线程函数接收,若不想返回信息,可填NULL。

等待线程:就是主线程需要进行的操作,因为线程是共用主线程的同一片地址空间,所以假如主线程先结束,那么子线程也随即结束。所以要想主线程在子线程后面结束就必须有这个等待函数 函数如下:

#include <pthread.h>int pthread_join(pthread_t thread,void **retval);

参数:① thread 需要等待的线程对象

②retval 可以看出它是一个二级指针,二级指针需要传递一级指针,而且是void 类型的。用于接收结束线程函数返回的内容,可为NULL。

函数都知道了,我们来开始创建线程:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
void *func(); //初始化两个函数 用于建立两个线程
void *func1();
struct m_art  //定义一个结构体 用于传递需要传递的参数
{int a;char b;
};
int main(int argc, char *argv[])
{pthread_t thread;//定义两个线程对象pthread_t thread1;void *ch =NULL;  //定义两个retval用于装结束线程返回的内容void *ch1 =NULL;struct m_art t;  //定义相同类型的结构体变量名 并给成员赋值t.a=5;t.b='l';int n = pthread_create(&thread,NULL,func,&t); //创建线程第1个 传递结构体名&tif(n<0)        //判断线程是否创建成功{perror("pthread_create1");exit(-1);}int m = pthread_create(&thread1,NULL,func1,NULL);  //创建线程第2个 不传递参数 填NULL。if(m<0)             //判断线程是否创建成功{perror("pthread_create2");exit(-1);}// 以下为主线程pthread_join(thread,&ch);    //等待线程。printf("%s \n",(char *)ch);  //打印线程1的返回内容pthread_join(thread1,&ch1);printf("%s \n",(char *)ch1);return 0;
}
void *func(void *arg) //线程1
{       int i=10;struct m_art *t=(struct m_art*)arg;  //定义相同类型结构体指针接收结构体名while(i--){   if(i==4)  //提前结束线程条件{pthread_exit("pt");  //结束时返回"pt"}printf("mmmmmmmmmmmmm  %d  %c\n",t->a,t->b); 打印查看通过传递结构体传递的参数sleep(1);  //每隔一秒执行一次 便于观察}
}void *func1() //线程2.因不传递参数,可不填参数
{printf("ccccccccccc\n");pthread_exit("pt1");  //结束时返回"pt1"
}

接下来,我们要实现线程间通信,因为是共用一片地址空间,所以线程间通信可以通过全局变量来实现。因为通过全局变量来实,所以在一个线程使用全局变量的同时,其他线程也在访问该数据 那么一些线程就会遭到破坏。所以我们就可以通过线程的同步和线程的互斥来解决这个问题。

什么是同步和互斥,如何让线程间同步或线程间互斥?

同步:多个线程之间,按照约定的顺序先后来执行程序。

同步中我们有一个东西叫做信号量,各个线程想要执行这个对应的程序就必须要有信号量,若信号量为0;则进入阻塞状态。操作信号量必须通过特定的函数接口才能访问,不可用运算符去直接操作。信号量本质时一个非负整数。

初始化信号量:

#include <semaphore.h>int sem_init(set_t *sem,int pshared,unsigned int value);

参数:

① sem 信号量,类似线程对象,需要定义set_t 类型。

② pshared 用于线程间同步 须填0。

③ value  信号量初始值。

线程运行时信号量时随需要改变的,所以信号量申请有释放。

信号量申请(P操作):

#include <semaphore.h>int sem_wait(set_t *sem);

调用一次申请一个数据量。

信号量释放(V操作):

#include <semaphore.h>int sem_post(set_t *sem);

调用一次释放一个数据量

接下来看看如何让线程同步:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
void *func();
void *func1();
char buf[64]={0}; //定义全局变量 buf缓冲区
sem_t sem1,sem2;  //定义两个信号量int main(int argc, char *argv[])
{pthread_t thread;  //定义线程对象pthread_t thread1;sem_init(&sem1,0,0); //初始化信号量 资源为0sem_init(&sem2,0,1); //初始化信号量 资源为1int n = pthread_create(&thread,NULL,func,NULL);  //创建线程1if(n<0){perror("pthread_create1");exit(-1);}int m = pthread_create(&thread1,NULL,func1,NULL); //创建线程2if(m<0){perror("pthread_create2");exit(-1);}pthread_join(thread,NULL); //等待线程pthread_join(thread1,NULL);return 0;
}
void *func()  //线程1
{       while(1){sem_wait(&sem1); //等待信号量(资源)一开始有0个,进入阻塞状态,去执行下一个线程,直到信号量sem1有资源。fputs(buf,stdout); //打印全局变量buf缓冲区里的内容;sem_post(&sem2); //给sem2申请信号量 让其变为1。}
}void *func1()  /线程2
{while(1){sem_wait(&sem2); //等待信号量(资源)一开始有1个,执行此线程,然后sem2信号量0.fgets(buf,64,stdin); //等待输入sem_post(&sem1);  //给sem1申请信号量,让其变为1。}
}

如此,我们就可以实现线程间通信了。

互斥:当一个线程使用共用数据时 其他线程都不能使用该数据。

多个线程能够共同访问的数据叫做临界资源。涉及到临界资源的代码块叫做临界区,互斥便是用互斥锁保护临界区。

让线程同步同样有三个函数:

①  互斥锁的初始化

#include <pthread.h>int pthread_mutex_init(pathread_mutex_t *mutex,pthread_mutex_t *attr);

参数:

mutex--互斥锁,同信号量对象,线程对象一样定义。

attr--填NULL。

返回值:

成功返回0,失败返回-1。

②  申请锁(上锁):

#include <pthread.h>int pthread_mutex_lock(pathread_mutex_t *mutex);

③  释放锁(解锁):

#include <pthread.h>int pthread_mutex_unlock(pathread_mutex_t *mutex);

接下来看看如何让线程互斥:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
void *func();                //大部分同上 相同部分不做过多解释
void *func1();
int num1=0,num2=0,sum=0;
pthread_mutex_t mutex;    //定义锁
int main(int argc, char *argv[])
{pthread_t thread;pthread_t thread1;pthread_mutex_init(&mutex,NULL);    //初始化锁int n = pthread_create(&thread,NULL,func,NULL);if(n<0){perror("pthread_create1");exit(-1);}int m = pthread_create(&thread1,NULL,func1,NULL);if(m<0){perror("pthread_create2");exit(-1);}pthread_join(thread,NULL);pthread_join(thread1,NULL);return 0;
}
void *func()
{       while(1){pthread_mutex_lock(&mutex); //上锁num1=sum;num2=sum; // 在此期间的数据据改变过程中 即使时间片结束,下一个线程也访问不到次期间的数据。sum++;pthread_mutex_unlock(&mutex); //解锁}
}void *func1()
{while(1){pthread_mutex_lock(&mutex);//上锁if(num1 != num2)  //按理来讲上个线程在第一句赋值之后有可能时间片在此结束,则导致num1和num2不同
//在此会打印,但由于上了锁,只能访问上一次的值,而上一次的值又是相等的,所以这里永远不会打印{printf("num1=%d  num2=%d\n",num1,num2);pthread_mutex_unlock(&mutex); //解锁}}
}

好了 各位读者姥爷,今天的分享到此结束了,哪里写的不对的地方欢迎多多指正,我们明天继续学习,感谢观看。

线程的创建与线程间通信(C语言)相关推荐

  1. linux c 线程的创建、线程等待、线程终止、线程分离

    1. 什么是线程   线程是进程执行内部的一个执行分支,在一个进程内部运行的多种执行流:内部本质上是多个线程在同一个地址空间运行:第一个pcb称之为主线程:有多个线程就有多个执行流:一个进程至少有一个 ...

  2. Java——线程的创建,线程池

    线程 多线程就是一个程序中有多个线程在同时执行. 多线程下CPU的工作原理 实际上,CPU(中央处理器)使用抢占式调度模式在多个线程间进行着高速的切换.对于CPU的一个核而言,某个时刻,只能执行一个线 ...

  3. 你知道线程池创建多少线程比较合理吗?

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者 | 小鱼儿_karl 来源 | https://w ...

  4. burp爆破线程设置多少_你知道线程池创建多少线程比较合理吗?

    为什么会使用多线程 创建多少线程比较合适 结束语 <Java 2019 超神之路> <Dubbo 实现原理与源码解析 -- 精品合集> <Spring 实现原理与源码解析 ...

  5. VC++中多线程学习(MFC多线程)一(线程的创建、线程函数如何调用类成员呢?如何调用主对话框的成员?、MFC中的工作线程和界面线程的区别)

    这里废话不多讲了,因为项目原因,需要开启线程进行处理,在不了解线程的情况下,直接百度一下,然后就使用了,结果可想而知,出现了异常,所以花了一天时间系统学习一下多线程,这里主要是针对win32编程方面的 ...

  6. java怎么创建子线程_Java创建子线程的两种方法

    摘要: 其实两种方法归结起来看还是一种,都是利用Thread的构造器进行创建,区别就是一种是无参的,一种是有参的. 一.继承Thread线程类: 通过继承Thread类,重写run方法,子类对象就可以 ...

  7. 线程的创建 验证线程之间共享数据 守护线程 线程进程效率对比 锁 死锁 递归锁...

    线程(from threading import Thread):CPU调度的最小单位 线程的两种创建方式:方式一: 1 from threading import Thread 2 def f1(i ...

  8. java 多线程编程(包括创建线程的三种方式、线程的生命周期、线程的调度策略、线程同步、线程通信、线程池、死锁等)

    1 多线程的基础知识 1.1 单核CPU和多核CPU 单核CPU,其实是一种假的多线程,因为在一个时间单元内,也只能执行一个线程的任务.微观上这些程序是分时的交替运行,只不过是给人的感觉是同时运行,那 ...

  9. java并发编程第一课 线程的创建、停止和状态变更

    开篇词: 由点及面,搭建你的 Java 并发知识网 你好,欢迎学习<Java 并发编程核心 78 讲>,我是讲师星星,一线互联网公司资深研发工程师,参与过集团内多个重点项目的设计与开发. ...

最新文章

  1. SpringCloud版本命名
  2. Java Integer类lowerOneBit()方法与示例
  3. 学会用Go解析复杂JSON的思路
  4. 【Java】TCP Socket编程案例——文件传输聊天工具
  5. CCF201403试题
  6. Android之MVC模式
  7. 图纸管理软件_企业图纸文档的安全管理与使用,是否遇到这些图纸管理问题?...
  8. Mac IDEA切换主题颜色
  9. DirectX 修复
  10. c语言计算标准体重作业,c语言/* 已知成人标准体重粗算公式:
  11. 怎么样开启移动热点?看我操作,简单开启
  12. Sql Server数据库被置疑后解决方法
  13. 笔记本当服务器显示屏,笔记本当服务器屏幕设置
  14. 三种网络仿真软件:OPNET、NS和GloMoSim
  15. 用python来处理待打印的深色背景图片
  16. Android小项目集合100多个
  17. SWAPIDC主机系统 对接三方银行支付接口扩展插件 微信+支付宝接口
  18. 科学计算机自动显示小数,卡西欧计算器FX-82MS怎么能让计算器永久显示小数计数而不是科学技术法啊?好烦啊,每次都是显示科学技术法,我试过SHI...
  19. Win11测试麦克风方法介绍
  20. Java写个简单的记事本()

热门文章

  1. 正则表达式校验新疆人姓名格式
  2. 学生宿舍管理项目开发计划书_1学生信息管理系统项目开发计划书
  3. 前端HTML5+CSS3静态页面开发-京东首页
  4. js中的caller属性和callee属性
  5. 浅析无约束优化的方法
  6. Mr.张小白(案例:基于Spring MVC实现文件上传和下载)
  7. 新能源汽车核心技术-VCU硬件在环(HiL)仿真测试系统
  8. Google Play services for Android下载:失败,华为系统限定死了,上网找了几种方法全部失败
  9. 万两:绘程BVC,从对冲基金到家族投资的“双翼”“全垒王”
  10. 淘宝搜索关键词 关键词的种类 根据宝贝权重选词 判断是否为假词