Linux——线程使用及互斥量

线程的基本操作

概念
线程是程序中的一个执行路线。每个程序当中至少有一个线程。
程序在执行的过程中是逐条执行的,按照代码的逻辑一次向下执行,所以无法同时完成两条指令,故而引进了线程,举个很简单的例子,如果同时进行两个死循环,用单线程的话只能进行一个死循环,另一个死循环永远也不会执行,故而用多线程就可以解决这个问题。在学习网络cs模型时更能体现线程的作用,因为你需要在发送数据的同时接收数据。

创建线程

 int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);参数thread:返回线程IDattr:设置线程的属性,attr为NULL表示使用默认属性start_routine:是个函数地址,线程启动后要执行的函数arg:传给线程启动函数的参数返回值:成功返回0;失败返回错误码	

获取线程ID

	pthread_t pthread_self(void)返回值返回调用该函数的线程的ID

线程终止

void pthread_exit(void *value_ptr);
参数value_ptr:value_ptr不要指向一个局部变量。
该函数无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)int pthread_cancel(pthread_t thread);
参数thread:线程ID
返回值:成功返回0;失败返回错误码

线程等待

int pthread_join(pthread_t thread, void **value_ptr);
参数thread:线程IDvalue_ptr:它指向一个指针,后者指向线程的返回值
返回值:成功返回0;失败返回错误码

为何要线程等待?线程退出时,其占有的空间资源并不会释放掉,在次创建一个线程时该空间资源并不会被再次利用,也就是说会造成内存泄漏,故而需要线程等待。其中线程等待函数中的参数value_ptr指向线程IDthread的线程返回值。如果thread线程被别的线程调用pthread_ cancel异常终掉,value_ ptr所指向的单元里存放的是常数PTHREAD_ CANCELED;如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。

线程分离

int pthread_detach(pthread_t thread);
参数thread:线程ID
返回值:成功返回0,错误返回错误号

线程分离与线程等待很相似,他们都能释放掉已结束线程占用的空间资源,但线程分离不关心线程退出的返回值,只要线程退出,就自动释放线程资源。

编译指令

gcc 123.c -o out -lpthread
注:编译多线程的程序一定要加上 -lpthread

示例:

#include<pthread.h>
#include<stdio.h>
#include<unistd.h>
void *fun(void *num)
{int i=0;for(;i<30;i++){printf("a");if(i==25){pthread_exit(NULL);//输出25个a后线程终止}usleep(1);//延时1us}
}int main()
{pthread_t ptid;if(pthread_create(&ptid,NULL,fun,NULL)!=0)//创建线程{return 0;}int i=0;for(;i<10;i++){if(i%2==0){printf("b");}usleep(2);//延时2us}printf("\n");pthread_join(ptid,NULL);线程等待return 0;
}

结果
在这里插入图片描述
主线程在执行5个b后,输出结束,此时等待子线程结束,即第二行结果显示,至于第一行为什么a、b都有输出,是因为线程在执行时是同时进行的,主线程输出一个b后睡眠2us,在这段时间子线程开始输出a,因为子线程也有1us睡眠,限制其执行速度,当主线程的2us时间到后,又开始主线程输出b。

线程互斥

我们先看看几个概念:

临界资源:多线程执行流共享的资源就叫做临界资源
临界区:每个线程内部,访问临界资源的代码,就叫做临界区
互斥:任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对临界资源起保护作
用
原子性:不会被任何调度机制打断的操作,该操作只有两态,要么完成,要么未完成

那到底什么是线程互斥呢?不要着急,我们先看看下面这个抢票的例子:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sched.h>int ticket = 100;//共有100张票void *route(void *arg)
{char *id=(char *)arg;while(1){if(ticket>0){usleep(1000);printf("%s sells ticket:%d\n",id,ticket);ticket--;//抢到票就减1}else{break;		}}
}
int main()
{pthread_t t1,t2,t3,t4;pthread_create(&t1,NULL,route,(void *)"thread 1");pthread_create(&t2,NULL,route,(void *)"thread 2");pthread_create(&t3,NULL,route,(void *)"thread 3");pthread_create(&t4,NULL,route,(void *)"thread 4");pthread_join(t1,NULL);pthread_join(t2,NULL);pthread_join(t3,NULL);pthread_join(t4,NULL);return 0;}

结果
在这里插入图片描述
结果让人感到意外,怎么会有负数呢?因为线程是同时进行的,当进行到票数为1的时候,此时部分线程已经经过了**if(ticket>0)**的判断,其中有一个线程的执行速度比较快,他抢先拿到了最后的一张票,此时票数为0,因为此时并不止这一个线程经过了判断,他们也能强到一张票,故而出现负数。原理图如下:
在这里插入图片描述

为避免上述现象的出现,引进了互斥量mutex

互斥量初始化

静态分配
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;动态分配
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);参数:mutex:要初始化的互斥量attr:NULL

互斥量加锁与解锁

加锁:
int pthread_mutex_lock(pthread_mutex_t *mutex);解锁:
int pthread_mutex_unlock(pthread_mutex_t *mutex);
注:加锁后一定要记得解锁二者的参数相同,都是指初始化后的互斥量

互斥量销毁

int pthread_mutex_destroy(pthread_mutex_t *mutex);
参数:需要销毁的互斥量
注:静态分配的互斥量不需要销毁,已经枷锁的互斥量不能销毁

使用方法

  1. 在使用前先初始化
  2. 访问临界资源前加锁
  3. 访问临界资源后解锁
  4. 互斥量使用结束后销毁

改进后代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sched.h>int ticket = 100;//票
pthread_mutex_t mutex;//定义互斥量void *route(void *arg)
{char *id=(char *)arg;while(1){pthread_mutex_lock(&mutex);//加锁if(ticket>0){printf("%s sells ticket:%d\n",id,ticket);ticket--;pthread_mutex_unlock(&mutex);//解锁}else{pthread_mutex_unlock(&mutex);//解锁break;}usleep(2);}
}
int main()
{pthread_t t1,t2,t3,t4;pthread_mutex_init(&mutex,NULL);//初始化互斥量pthread_create(&t1,NULL,route,(void *)"thread 1");pthread_create(&t2,NULL,route,(void *)"thread 2");pthread_create(&t3,NULL,route,(void *)"thread 3");pthread_create(&t4,NULL,route,(void *)"thread 4");pthread_join(t1,NULL);pthread_join(t2,NULL);pthread_join(t3,NULL);pthread_join(t4,NULL);pthread_mutex_destroy(&mutex);//互斥量销毁return 0;}

结果
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/545659.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

安卓安装kali linux之Termux

本文讲述如何在手机上安装kali linux,我本想安装其他版本的linux,但不知是什么原因安装到一半就卡住&#xff0c;最终安装kali成功了&#xff0c;但也只是安装了kali的壳子&#xff0c;在inux上的操作都可以实现&#xff0c;只是工具并没有安装&#xff0c;后期可以自主安装工具…

液晶显示温度(DS18B20)

DS18B20测温范围-55——125度&#xff0c;在-10——85度之间精度为0.5度&#xff0c;其测温精度还是较高的&#xff0c;DS18B20常见封装为3个引脚&#xff0c;VCC(电源正)&#xff0c;DQ(信号线)&#xff0c;GND(电源负)&#xff0c;如图&#xff1a; DS18B20相关指令&#xf…

第 5-5 课:线程安全——synchronized 和 ReentrantLock + 面试题

前面我们介绍了很多关于多线程的内容,在多线程中有一个很重要的课题需要我们攻克,那就是线程安全问题。线程安全问题指的是在多线程中,各线程之间因为同时操作所产生的数据污染或其他非预期的程序运行结果。 线程安全 1)非线程安全事例 比如 A 和 B 同时给 C 转账的问题…

MFC中的几个常用类——CFileDialog

2019独角兽企业重金招聘Python工程师标准>>> 1 简介 CFileDialog类封装了Windows常用的文件对话框。常用的文件对话框提供了一种简单的与Windows标准相一致的文件打开和文件存盘对话框功能。 可以用 构造函数提供的方式使用CFileDialog&#xff0c;也可以从CFileDi…

Exchange Server2010部署完后的配置:CA、Outlook Anywhere、OWA域名简写

Exchange Server 2010邮件系统安装完成后&#xff0c;必须经过相应的配置后&#xff0c;才能使Exchange Server 2010邮件系统提供基本的访问、邮件收发等基本功能。下面我们逐一看看如何让Exchanger Server跑起来。Exchange Server2010产品授权&#xff1a;我们目前所安装的Exc…

STM32——PID恒温控制

原理 元件 stm32f103核心板、L298N模块(当然用MOS管更好)、led一个、NPN三极管一个、蜂鸣器一个、DHT11一个、LCD1602一个、电阻200欧两个、可调电阻10K一个、加热丝一个 功能描述 用DHT11检测当前环境温湿度&#xff0c;并将数据显示在LCD1602上&#xff0c;在用设定温度与当…

第 6-2 课:SpringMVC 核心 + 面试题

Spring MVC 介绍 Spring MVC(Spring Web MVC)是 Spring Framework 提供的 Web 组件,它的实现基于 MVC 的设计模式:Controller(控制层)、Model(模型层)、View(视图层),提供了前端路由映射、视图解析等功能,让 Java Web 开发变得更加简单,也属于 Java 开发中必须要…

Lync2013 升级错误总结8 Lync2013 日志总是提示进程 RtcHost(5724) 收到了一个无效的客户端证书...

错误提示&#xff1a;解决方法&#xff1a;1打开注册表引导到&#xff1a;HKLM\System\CurrentControlSet\Control\SecurityProviders\Schannel2 新建一个DWORD键值&#xff1a;值的名称&#xff1a;EnableSessionTicket3 右键这个值点编辑讲数值数据修改成&#xff1a;24 重新…

简易的遍历文件加密解密

功能描述 将生成的可执行程序放在指定的文件夹内&#xff0c;双击后将该目录下所有文件包括子文件夹内文件全部加密&#xff0c;再次双击运行后将进行解密。 加密解密实现 主要运用了异或与取反操作&#xff0c;异或&#xff1a;两个值不同为1&#xff0c;相同为0。取反就是将该…

安卓手机使用linux(含图形界面)——Aid Learning

以前再安卓手机上使用linux系统都是使用Termux&#xff0c;安装上很麻烦&#xff0c;而且还是黑乎乎的窗口&#xff0c;没有图形界面&#xff0c;对于初学linux者来说并不友好&#xff0c;而Aid Learning就更人性化了&#xff0c;他是一种模拟的linux,其安装十分简易&#xff0…

简易花式流水灯

先看看效果 具体思路 实现流水灯的效果其实就是控制相应的I/O口&#xff0c;以P2为例&#xff0c;通过有规律的改变P2各I/O口的状态就可实现相应规律的流水灯效果&#xff0c;这其中需要用到与、或、异或、左移、右移等操作。   流水灯向左闪烁点亮就是将P2最低位的1不断左移…

STM32——直流电机PI调速

所需元件 STM32F103开发板、L298N一个、带编码器的直流电机一个&#xff08;如下图所示&#xff0c;淘宝上有很多&#xff09; 系统框图 通过系统框图&#xff0c;我们需要做两件事&#xff0c;一是要测速&#xff0c;二是要调节。测速目前流行的就是通过编码器测速&#xff…

JAVA设计模式--简单介绍

2019独角兽企业重金招聘Python工程师标准>>> &#xfeff;一、简介 Design pattern 是众多软件开发人员经过漫长的试验和错误总结出来的在软件开发过程中面临一般问题的解决方案&#xff0c;代表着最佳实践。使用设计模式是为了重用代码、让代码更容易被他人理解、保…

为什么是PID控制

在进入正式话题之前需要引入四个概念&#xff1a;稳态误差、终值定理、幅角条件和系统稳定的充要条件。 稳态误差&#xff1a;系统达到稳定状态后&#xff0c;系统的实际输出量与系统希望的输出量之间的偏差。 终值定理&#xff1a;设有连续函数f(t)f(t)f(t)&#xff0c;当t趋于…

卡尔曼滤波器推导

注&#xff1a;受控制领域大牛CAN博士启发&#xff0c;受益匪浅&#xff0c;作此文以为笔记。 简介 设 卡尔曼滤波器是从测量值ZZZk的平均数开始的。开始推导&#xff1a; 由上式可知   也就是说随着kkk的增大&#xff0c;测量结果Zk不在重要&#xff0c;因为已经获得了足…

cocos2dx3.2文件结构和代码结构

既然选定了cocos2dxlua的原生方式来开发&#xff0c;首先要确定的是使用哪个版本的cocos2dx&#xff0c;先看看github上的changelog和releasenote&#xff0c;然后在google里搜索一下&#xff0c;参考了jacky的博客http://zengrong.net/post/2100.htm&#xff0c;最终选择了coc…

改进的PID算法

位置式PID算法 位置式PIDPIDPID算法是一种比较直观的的PIDPIDPID算法&#xff0c;如系统框图中所示&#xff0c;ininin表示设定值&#xff0c;errorerrorerror表示差值&#xff0c;uuu表示控制器输出值&#xff0c;outoutout表示被控量。算法表达式如下&#xff1a; 增量式PI…

几种简单电路知识汇总

这篇文章用于记录平时设计电路或者在书中遇到的一些电路方面的知识&#xff0c;会不定期更新。就先从运算放大器开始&#xff0c;对此做个简单的介绍。 运算放大器 说到运算放大器就不得不说两个概念&#xff0c;虚短与虚断。 虚短&#xff1a; 在理想情况下&#xff0c;运算…

51单片机——交通灯

原理图 功能描述 1、基本功能就是如同红绿灯一般&#xff0c;不做赘述。   2、红灯时长和绿灯时长可通过按键设置&#xff0c;即按键列中的上面4个&#xff0c;当这4个按键有一个按下后便进入时长设置功能&#xff0c;设置完成后按最下面两个按键&#xff08;紧急控制按钮&am…

设置TextField内文字距左边框的距离

2019独角兽企业重金招聘Python工程师标准>>> //设置文本框左边的viewUITextField *textField [[UITextField alloc]init];textField.frame CGRectMake(10, 30, 300, 30);[self.view addSubview:textField];textField.leftView [[UIView alloc]initWithFrame:CGRe…