【多线程】c语言中的多线程_Loren灬的博客-程序员信息网_c语言多线程

技术标签:   c++  c  多线程  死锁  【多线程】  

线程 Thread:专业术语称之为程序执行流的最小单元 。线程是不会执行程序的,可以理解成线程就是一个载体,将 要执行的代码 运送到CPU进行处理。
多线程就是多个线程同时并发执行

1. 为什么用多线程?

1). 避免拥塞
单个线程中的程序,是按照顺序执行的,排在前面的程序如果发生异常卡住(阻塞),会影响到后面的程序执行。多线程就等于是异步调用,避免这个情况。
2) .避免CPU的空转
这个比如一个网页,如果是单线程的话,服务器处理一条请求后,会等待下一个请求,这时候CPU处于一个闲置的状态。多线程能避免这个问题。
3).提升效率
避免了1,2的问题,效率自然就提高了,归根结底也是为了这点。

2.线程与进程的区别

进程是系统进行资源分配和调度的一个独立单位,线程是进程的一个实体,是CPU调度和分派的基本单位,线程只是一段程序的执行。

比如打开了一个软件(比如说QQ),能从任务管理器,就能看到有QQ这样一个进程,这时候想跟别人聊个天,打开对话框,这就是运行一个线程;查看一下聊天的这个人的资料,这又运行了另外一个线程。

同一个进程下的线程是资源共享的,进程与进程直间都是独立的。

3.用例

  • 创建一个线程,看 example0.c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h> 
// 使用线程时需要添加<pthread.h>这个头文件
// myfunc 线程携带的函数;固定为Void*型
myfunc(void* args){
    
		printf("hello world\n");
	return NULL;
}
 
int main(){
    
    //  声明线程th1 
    pthread_t th1; 
    pthread_create(&th1,NULL,(void*)myfunc,NULL); 
    pthread_join(th1,NULL)}

C语言的程序中要使用线程,需要添加<pthread.h>这个头文件
myfunc()是这个线程要携带执行的程序 ,函数与函数的参数,都是必须是 void* 类 ,涉及到调用和传值时需要进行类型强转。

主函数中,先对线程进行一个声明, pthread_t th1; 使用 pthread_create()对th1进行创建。

pthread_create()中有4个参数

1要创建的线程id(th1); 2设置线程的属性(没有特殊需求时填NULL就可以了) ; 3线程要运行的函数的地址(myfunc);4是要向运行的那个函数传参(myfunc()中的args),args 也是void*类型。

pthread_join()函数的功能是等待一个线程的结束,它是一个线程阻塞的函数。

pthread_join有两个参数:指定要等待的线程id;接收线程函数的返回值。

运行这个程序看看
在这里插入图片描述
如果没有pthread_join,终端可能会没有打印,因为主函数执行结束,线程中的函数还没有执行完成。

  • 一个多线程的例子,来演示说明下线程的是并发执行看 example1.c
// 创建两个线程  
void* myfunc(void* args){
    
	int i;
	char* name = (char*)args;
	for(i=1;i<50;i++){
    
		printf("%s:%d\n",name,i);
	}
	return NULL;
}
 
int main(){
    
 
	pthread_t th1;
	pthread_t th2;
	
	pthread_create(&th1,NULL,myfunc,"th1");
	pthread_create(&th2,NULL,myfunc,"th2");
 
	pthread_join(th1,NULL);
	pthread_join(th2,NULL);	
 
}

两个线程,都去调用这个myfunc()函数,运行结果如下:
在这里插入图片描述
从这个程序的运行结果,能看出th1运行到17时,th2开始运行了,而这时th1没有继续打印,能看出th1与th2是并发执行的。两个线程的执行顺序是不确定的,重复运行example1.c这个程序的结果也会不同。

这个程序里使用到了pthread_create()中的第四个参数向 myfunc传参。

  • 看 example2.c
int s =0;
 
void* myfunc(void* args){
    
	int i =0;
	for(i=0;i<10000;i++){
    
	s++;	
	}
	return NULL;
}
int main(){
    
	pthread_t th1;
	pthread_t th2;
 
	pthread_create(&th1,NULL,myfunc,NULL);
	pthread_create(&th2,NULL,myfunc,NULL);
 
	pthread_join(th1,NULL);
	pthread_join(th2,NULL);
 
	printf("s=%d\n",s);
 
	return 0;
}
// 用两个线程去执行myfunc函数 , 理想值应该为20000 

编译后运行结果如下:
在这里插入图片描述这个函数里用了一个全局变量s,执行函数是一个10000的累加,理想的运行结果应该是20000,我这里运行了3次这个程序,每次的结果都不同。 用这个演示来表示一下,多线程之间是资源共享的。

s++ 是有三个步的,读取s,s+1,写入s。

在程序运行的某个时刻,th1携带myfunc执行s++,读取s,此时s=100,进行s+1, 与此同时th2也开始读取s,此时的s还是等于100, 这时th1,执行写入s=101,th2执行s++,写入s ,s=101. th2中的s 就会覆盖掉 th1中的s 。这样造成了结果的误差。

  • 对example2.c中的线程进行一个加锁,看 example3.c
int s =0;
 
pthread_mutex_t lock;   //定义一个锁
 
void* myfunc(void* args){
    
	pthread_mutex_lock(&lock); //上锁
	int i =0;
	for(i=0;i<10000;i++){
    
	s++;	
	}
	pthread_mutex_unlock(&lock);//解锁
	return NULL;
}
int main(){
    
	pthread_t th1;
	pthread_t th2;
 
	pthread_mutex_init(&lock,NULL); //初始化lock这个锁	
	
	pthread_create(&th1,NULL,myfunc,NULL);
	pthread_create(&th2,NULL,myfunc,NULL);
 
	pthread_join(th1,NULL);
	pthread_join(th2,NULL);
 
	printf("s=%d\n",s);
 
	return 0;
}

运行结果:
在这里插入图片描述

锁的作用是什么呢?

前面说过多线程是并发执行的,th1运行后 进行了加锁,th2这时候想要运行,就必须等待th1解锁之后才行。
(一个卫生间,多个人要用,第一个人进去之后,把门锁上了,后边的人就得排队等着,第一个方便完了,解锁开门出来,第二个人进去,继续锁门……)

锁 在提高程序的安全性的同时,也降低了程序的效率。

锁的使用方法

pthread_mutex_t lock; 声明一个锁

pthread_mutex_init(&lock,NULL); 对声明的锁进行初始化

pthread_mutex_lock(&lock); //上锁 此时其他线程就开始等待

pthread_mutex_lock(&unlock); //解锁 其他线程可以使用资源了

死锁!
拿上边举例,th1运行后,th2会等待th1解锁,才能运行,如果程序出现错误中断了,th1没有执行完,重新启动后,th1又重新执行,这时候th2排在th1前边,需要等待th1的解锁 ,而th1又在等待th2结束。 这样就造成了相互等待的情况,这个就是死锁。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/yizhizainulii/article/details/124387556

智能推荐

Django中使用Celery异步及定时任务(附赠代码)_我就叫贝塔的博客-程序员信息网_django celery定时异步任务 示例

一、简介Celery是由Python开发、简单、灵活、可靠的分布式任务队列,其本质是生产者消费者模型,生产者发送任务到消息队列,消费者负责处理任务。Celery侧重于实时操作,但对调度支持也很好,其每天可以处理数以百万计的任务。特点:简单:熟悉celery的工作流程后,配置使用简单高可用:当任务执行失败或执行过程中发生连接中断,celery会自动尝试重新执行任务快速:一个单进程的celery每分钟可处理上百万个任务灵活:几乎celery的各个组件都可以被扩展及自定制应用场景举例:1.web应用

Kubernetes v1.22.1部署报错2:error: Get “http://localhost:10248/healthz“: dial t..._非著名运维的博客-程序员信息网

环境介绍:master 192.168.2.18node1 192.168.2.19node2 192.168.2.20CentOS 7.5Docker 19.03.132核+CPU,2GB+内存报错信息: 初始化Kubernetes时出现下面报错[[email protected] ~]# kubeadm init --kubernetes-version=v1.22.1 --apiserver-advertise-address=192.168.1.18 --image-repositor

初识SAP WM(转载自Kanter Wang)_superying的博客-程序员信息网_sap wm

最近集团的瑞士公司有可能要实施WM,而俺又很久没写技术性的BLOG了,因此WM的文章就这样产生了 。简述WM模块什么是WM,全称就是Warehouse Management(仓库管理)有的人很好奇,会说那MM是什么?MM当然不是“妹妹”,而是Material ManagementMM主要包含采购、货仓、发票三大模块那这就奇怪了,MM有个货仓,而WM又叫仓库,或许是这翻译问题,说...

Android 启动插件Activity的原理以及案例演示_hujin2017的博客-程序员信息网

当应用的apk文件安装到手机系统时,这个文件会被解压后存储到data/dalvik-cache 这个目录下,当app应用程序启动时,如果app所在的进程未创建,则会通过Zygote进程去fork一个子进程作为要启动的app的进程,并创建PathClassLoader。app进程启动后,会通过通过ClassLoader来完成dex文件的加载。PathClassLoader是继承BaseDexClas...

CS5266中文规格书|Capstone CS5266中文设计资料|TYPEC转HDMI带PD3.0+USB3.1拓展坞转换方案资料_QQ1176845380的博客-程序员信息网

CS5266AN Datasheet USB Type-C to HDMI2.0b [email protected] Converterwith PD3.0 and USB 3.1 Support1简介Capstone CS5266AN是一款高性能type-C/DP1.4至HDMI2.0b转换器,设计用于将USB Type-C源或DP1.4源连接至HDMI2.0b接收器。CS5266AN集成了一个兼容DP1.4的接收器HDMI2.0b兼容发射机。此外,还包括两个CC控制器,用于CC通信,以实现DP ...

PostgreSQL和PostGIS安装以及如何连接到ArcGIS pro_芝麻团坚果的博客-程序员信息网

创建空间数据库用pgAdmin 4图形界面完成创建。1 打开Admin 4,位于开始-&gt;所有程序-&gt;PostgreSQL 13下面,因为我安装的postgreSQL是13.多少的版本2 登录到服务器,即连接,登录帐号为超级用户postgres和自己在安装时候设置的密码。3 创建空间数据库。新建数据库,设置数据库名为Chicago,用户为postgres。切换到Definition面板。Encoding: UTF-8Template: postgis_31_sample(安装时创建的

随便推点

如何在网页中动态显示当前的系统日期和时间?_yehaiping1214的博客-程序员信息网

1、客户端的当前时间        setInterval("document.all.sTime.innerText=new   Date().toLocaleString()",300)2、服务端的当前时间   dim   week       week   =   Array("一","二","三","四","五","六","日")     Response.Write(year(dat

中断和异常的处理_ProgrammingRing的博客-程序员信息网_异常处理段故障

本文为 第17章笔记中断和异常中断和异常概述中断和异常的作用是指示系统中的某个地方发生一些事件, 需要引起处理器(包括正在执行中的程序和任务)的注意. 当中断和异常发生时, 典型的结果是迫使处理器将控制从当前正在执行的程序或任务转移到另一个历程或任务中去. 该例程叫做中断处理程序, 或者异常处理程序. 如果是一个任务, 则发生任务切换.1. 中断(Interrupt)

序列流SequenceInputStream将三个文件信息合并至一个文件中_充满鲜花的世界到底在哪里的博客-程序员信息网_sequenceinputstring

package demo.io;import java.io.*;import java.util.*;/** * 将三个文件信息合并至一个文件中 */class 序列流SequenceInputStream { public static void main(String[] args) { Vector&lt;FileInputStream&gt; fileVec = new Vector&lt;&gt;();//方法1 ArrayList&lt;

【工具】idea中Git强制拉取覆盖本地_嗨森bao的博客-程序员信息网_idea 强制pull

刚刚拉取主分支最新的代码出现Untracked Files Prevent MergeMove or commit them before merge解决方法:点击IDEA下面的Terminal输入命令:git fetch --all &amp;&amp; git reset --hard origin/master &amp;&amp; git pull

连载《一个程序猿的生命周期》- 31、与两个离职人员的闲聊,我们都应该有反思精神_一个程序猿的生命周期的博客-程序员信息网

一个程序猿的生命周期 微信平台口    号:职业交流,职业规划;面对现实,用心去交流、感悟。 公众号:iterlifetime二维码:  百木-ITer职业交流奋斗 群:141588103            注:与《30、争取内蒙区的市场销售》顺序互换一下。       如果想了解一位在职人员的真实想法是一件很不容易的事,这是一个普遍的现象,大家生怕