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,一经查实,立即删除!

相关文章

UNDO Tablespace

UNDO表空间用于存放UNDO数据&#xff0c;当执行DML操作&#xff08;insert、update、delete&#xff09;的时候&#xff0c;oracle会将这些操作的旧数据写入到UNDO段。UNDO数据也称为回滚数据&#xff0c;用于确保数据的一致性。作用包括&#xff1a;1、回退事务2、读一致性3、…

第 4-2 课:反射和动态代理 + 面试题

反射 反射机制是 Java 语言提供的一种基础功能,赋予程序在运行时自省(introspect)的能力。简单来说就是通过反射,可以在运行期间获取、检测和调用对象的属性和方法。 反射的使用场景 在现实中反射的使用场景有很多,比如以下几个。 使用场景一:编程工具 IDEA 或 Eclip…

出现奇数次的数字_查找出现奇数次的数字

出现奇数次的数字Problem statement 问题陈述 Given an array of positive numbers where all the numbers occur even number of times except only one number which occurs an odd number of times. We need to find the special number (number occurring an odd number o…

linux——常用指令

指令功能ls列出当前目录下所有子目录和文件pwd显示当前目录的路径cd 目录名进入该目录cd …返回上一级目录touch 文件名创建一个文件mkdir 文件夹名创建一个文件夹rmdir 文件名与mkdir相对&#xff0c;删除一个文件夹&#xff0c;但必须拥有对当前目录进行操作的权限rm -r删除目…

js中div显示和隐藏钮为什么页面总是跳一下到最上面

<div class"menu_left"> <ul > <li id"t1" style"background-image:url(images/t2.gif);" > <a href"#" id"first" οnclick"infoList(first);" >中心动态</a><…

ORA-00911:无效字符 错误及解决

今天写了一局sql语句&#xff0c;用来向Oracle数据库插入一条数据。我是这样写的……String sql "insert into userinfo(usermail,usernickname,userpassword)values(?,?,?);";结果出现ORA-00911:无效字符&#xff0c;解决方法是&#xff1a;………去掉sql语句最…

第 5-2 课:线程池——ThreadPoolExecutor + 面试题

线程池介绍 线程池(Thread Pool):把一个或多个线程通过统一的方式进行调度和重复使用的技术,避免了因为线程过多而带来使用上的开销。 为什么要使用线程池? 可重复使用已有线程,避免对象创建、消亡和过度切换的性能开销。避免创建大量同类线程所导致的资源过度竞争和内…

创建动画

1.动画&#xff08;头部-开始动画&#xff09; [UIView beginAnimations:nil context:nil];2.设置动画的执行时间 [UIView setAnimationDuration:1.0];3.向上移动// CGPoint tempCenter _btn.center; CGRect tempFrame _btn.frame;tempFrame.origin.y - 50;_btn.frame tempF…

安卓安装kali linux之Termux

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

常用的作业调度算法应用练习

单道环境下四个作业&#xff0c;它们进入系统的时间如下&#xff1a;作业进入时间估计运行时间&#xff08;分钟&#xff09;JOB18:00120JOB28:5050JOB39:0010JOB49:5020(1)给出FCFS , SJF&#xff0c;HRN下的作业执行次序(2)给出FCFS , SJF, HRN下的作业平均周转时间和带权平均…

第 5-1 课:线程与死锁 + 面试题

线程介绍 线程(Thread)是程序运行的执行单元,依托于进程存在。一个进程中可以包含多个线程,多线程可以共享一块内存空间和一组系统资源,因此线程之间的切换更加节省资源、更加轻量化,因而也被称为轻量级的进程。 什么是进程 进程(Processes)是程序的一次动态执行,是…

检查列表中的所有元素在Python中是否相同

Here, we are implementing a python program to check whether all elements of a list are the same or not? 在这里&#xff0c;我们正在实现一个python程序来检查列表中的所有元素是否相同&#xff1f; We can use [1:] and [:-1] to compare all the elements in the g…

调用百度人脸识别API进行人脸对比 C语言

百度人脸识别api使用是免费的&#xff0c;有人脸对比、人脸搜索、人脸检测与属性分析三个功能&#xff0c;本文写的是人脸对比。这里给出百度人脸对比api的技术文档&#xff0c;请点击网址https://cloud.baidu.com/doc/FACE/s/Lk37c1tpf 另外需要注册百度智能云&#xff0c;获取…

(自连接)SQL面试题-0608

一个简单的表TABLE 有100条以上的信息&#xff0c;其中包括&#xff1a;产品 颜色 数量产品1 红色 123产品1 蓝色 126产品2 蓝色 103产品2 红色 NULL产品2 红色…

SQL Server 2005 常用数据类型详解

1. 字符串数据类型char此数据类型可存储1~8000个定长字符串&#xff0c;字符串长度在创建时指定&#xff1b;如未指定&#xff0c;默认为char(1)。每个字符占用1byte存储空间。nchar此数据类型可存储1~4000个定长Unicode字符串&#xff0c;字符串长度在创建时指定&#xff1b;如…

第 5-4 课:ThreadLocal 详解 + 面试题

什么是 ThreadLocal? ThreadLocal 诞生于 JDK 1.2,用于解决多线程间的数据隔离问题。也就是说 ThreadLocal 会为每一个线程创建一个单独的变量副本。 ThreadLocal 有什么用? ThreadLocal 最典型的使用场景有两个: ThreadLocal 可以用来管理 Session,因为每个人的信息都…

Java LinkedList公共对象peek()方法(带示例)

LinkedList公共对象peek()方法 (LinkedList public Object peek() method) This method is available in package java.util.LinkedList.peek(). 软件包java.util.LinkedList.peek()中提供了此方法。 This method is used to retrieve the head element of the linked list wit…

舵机驱动

舵机的驱动是以PWM信号的占空比来控制的&#xff0c;该PWM信号的周期位20ms&#xff0c;宽度在0.5ms——2.5ms之间&#xff0c;驱使舵机转动角在0——180度之间&#xff0c;一些常用角度对应脉宽如下表&#xff1a; 舵机转动角脉宽00.5 ms451 ms901.5 ms1352 ms1802.5 ms 在实…

【网络流】【Dinic】【Next Array】Dinic模板

注意&#xff1a;有时加边不一定要加反向弧。 Next Array版。 1 #include<cstdio>2 #include<cstring>3 #include<algorithm>4 #include<queue>5 using namespace std;6 #define INF 21474836477 #define MAXN 200118 #define MAXM 6003019 int v[MAXM…

kotlin字符串数组_Kotlin程序读取,遍历,反向和排序字符串数组

kotlin字符串数组Given a string array, we have to read, traverse, reverse and sort its elements. 给定一个字符串数组&#xff0c;我们必须读取&#xff0c;遍历&#xff0c;反转和排序其元素。 Example: 例&#xff1a; Input:arr ["abc", "pqr",…