【Linux-day11-线程的创建与同步】

Linux 线程的创建与同步

线程的概念

线程是进程内部的一条执行序列或执行路径,一个进程可以包含多条线程。

进程与线程的区别

  • 进程是资源分配的最小单位,线程是 CPU 调度的最小单位
  • 进程有自己的独立地址空间,线程共享进程中的地址空间
  • 进程的创建消耗资源大,线程的创建相对较小
  • 进程的切换开销大,线程的切换开销相对较小

线程的实现方式

在操作系统中,线程的实现有以下三种方式:

  • 内核级线程
  • 用户级线程
  • 组合级线程

Linux 中线程的实现

Linux 实现线程的机制非常独特。从内核的角度来说,它并没有线程这个概念。Linux 把
所有的线程都当做进程来实现。内核并没有准备特别的调度算法或是定义特别的数据结构来
表征线程。相反,线程仅仅被视为一个与其他进程共享某些资源的进程。每个线程都拥有唯
一隶属于自己的 task_struct,所以在内核中,它看起来就像是一个普通的进程(只是线程和
其他一些进程共享某些资源,如地址空间)。

线程库中的接口介绍

pthread_create()用于创建线程

int pthread_create(pthread_t *thread, **const ** pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

成功返回 0, 失败返回错误码

thread: 接收创建的线程的 ID

attr: 指定线程的属性

start_routine: 指定线程函数

arg: 给线程函数传递的参数

pthread_exit()退出线程

int pthread_exit(void *retval);

pthread_exit()退出线程

retval:指定退出信息

pthread_join()等待 thread 指定的线程退出,线程未退出时,该方法阻塞

int pthread_join(pthread_t thread, void **retval);

retval:接收 thread 线程退出时,指定的退出信息

测试代码1

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>void* fun(void* arg)
{int* p = (int*)arg;int val = *p;printf("%d\n",val);return NULL;
}int main()
{pthread_t id[5];int i=0;for(;i<5;i++){pthread_create(&id[i],NULL,fun,(void*)&i);}for(i=0;i<5;i++){pthread_join(id[i],NULL);}exit(0);
}

预估:会出现0 1 2 3 4按一定顺序

结果如图:

我们发现结果和预想的不一样,这是因为线程是并发运行的,主线程main()和5个副线程都在同时运行,fun是通过指针解引用,主线程会随时改变i的值 ; pthread_create(&id[i],NULL,fun,(void*)&i);只是向内核申请,不一定顺序批准。

测试代码2

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
int val = 0;
void* fun(void* arg)
{for(int i=0;i<1000;i++){printf("%d\n",++val);}return NULL;
}
int main()
{pthread_t id[5];int i=0;for(;i<5;i++){pthread_create(&id[i],NULL,fun,(void*)&i);}for(i=0;i<5;i++){pthread_join(id[i],NULL);}exit(0);
}

预期: 输出5000

结果如图:

这是因为线程是并发执行的,上一个线程对val进行++完,还没有写回内存,下个进程读取了之前的值对其++;

线程同步

1. 信号量

函数介绍

int sem_init(sem_t * sem, int pshared,unsigned int value)

sem: 指向的信号量对象

pshared: 0表示此信号为当前进程局部的,否则为多个进程间共享

value: 设置信号的值

int sem_wait(sem_t * sem)

进行p操作,信号值减1

sem: 指向的信号量对象

int sem_post(sem_t * sem)

进行v操作,信号值加1

sem: 指向的信号量对象

int sem_destory(sem_t * sem)

j清理该信号拥有的所有资源,成功返回0

sem: 指向的信号量对象

下面使用该方法对测试代码2修改

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<semaphore.h>int val = 0;
sem_t sig;
void* fun(void* arg)
{for(int i=0;i<1000;i++){sem_wait(&sig);printf("%d\n",++val);sem_post(&sig);}return NULL;
}
int main()
{pthread_t id[5];sem_init(&sig,0,1);int i=0;for(;i<5;i++){pthread_create(&id[i],NULL,fun,(void*)&i);}for(i=0;i<5;i++){pthread_join(id[i],NULL);}sem_destroy(&sig);exit(0);
}

结果如图:

2.互斥锁

接口介绍:

1.int pthread_mutex_init(pthread_mutex_t* mutex,const pthread_mutexattr_t * mutexattr);

初始化锁
mutex: 互斥变量的指针
mutexattr: 设置互斥锁的属性

2.int pthread_mutex_lock(pthread_mutex_t* mutex);

上锁(加锁)

3.int pthread_mutex_unlock(pthread_mutex_t* mutex);

解锁

4.int pthread_mutex_destroy(pthread_mutex_t* mutex);

销毁锁

使用此方法修改测试代码2

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<semaphore.h>int val = 0;
pthread_mutex_t mutex;
void* fun(void* arg)
{for(int i=0;i<1000;i++){pthread_mutex_lock(&mutex);printf("%d\n",++val);pthread_mutex_unlock(&mutex);}return NULL;
}int main()
{pthread_t id[5];pthread_mutex_init(&mutex,NULL);int i=0;for(;i<5;i++){pthread_create(&id[i],NULL,fun,(void*)&i);}for(i=0;i<5;i++){pthread_join(id[i],NULL);}pthread_mutex_destroy(&mutex);exit(0);}

结果如图:

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

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

相关文章

数据结构基础7:二叉树【链式结构】实现和递归思想。

二叉树的链式结构实现 一.二叉树链式结构的实现&#xff1a;1.前置说明&#xff1a;1.创建二叉树&#xff1a;2.二叉树的结构&#xff1a; 2.二叉树的遍历&#xff1a;1.二叉树的前中后序遍历&#xff1a;2.内容拓展&#xff1a; 二.二叉树链式(题目)题目一&#xff1a;计算节点…

yolov7简化网络yaml配置文件

yolov7代码结构简单&#xff0c;效果还好&#xff0c;但是动辄超过70几个模块的配置文件对于想要对网络进行魔改的朋友还是不怎么友好的&#xff0c;使用最小的tiny也有77个模块 代码的整体结构简单&#xff0c;直接将ELAN结构化写成一个类就能像yolov5一样仅仅只有20几个模块&…

【操作系统】进程控制

进程控制&#xff1a;创建新进程&#xff0c;撤销已有进程&#xff0c;实现进程状态转换等。 原语&#xff1a;进程控制用的程序段。执行期间不允许中断&#xff0c;用&#xff02;关中断&#xff02;和&#xff02;开中断&#xff02;指令&#xff08;特权指令&#xff09;实…

Android性能优化之应用瘦身(APK瘦身)

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、人工智能等&#xff0c;希望大家多多支持。 目录 一、导读二、概览2.1 apk组成 三、优化方向3.1 源代码3.1.1 代码混…

Dedecms最新版--0day分享分析(二)

前言 接上一篇的Tricks&#xff0c;既然利用远程文件下载方式成为了实现RCE的最好方法&#xff0c;毕竟在执行的时候没有恶意shell文件&#xff0c;恶意木马被存放于远端服务器&#xff0c;那么下文的day就是对远程恶意文件的利用。 环境 下载最新版本&#xff1a; https://…

Kotlin Files Paths write ByteArray writeString写多行BufferedWriter

Kotlin Files Paths write ByteArray writeString写多行BufferedWriter import java.nio.file.Files import java.nio.file.Paths import java.nio.file.StandardOpenOptionfun main(args: Array<String>) {val filePath "./myfile.txt"val path Paths.get(…

ModuleNotFoundError: No module named ‘gevent‘

1、先确定pip版本&#xff1a; pip3 list: 看到没有gevent包 如果pip版本不是最新版可以使用命令python -m pip install --upgrade pip进行更新&#xff0c; 2、安装 pip3 install gevent 安装完成

Java密码学之加解密

前篇&#xff1a;Java密码学之数字签名_东皋长歌的博客-CSDN博客 日常开发中用的比较多的功能点&#xff0c;加解密数据&#xff0c;用Java实现也是很快很实用。 下面记录一下加解密数据的过程。 1&#xff0c;创建密钥对生成器 KeyPairGenerator keyPairGen KeyPairGener…

第29章_瑞萨MCU零基础入门系列教程之改进型环形缓冲区

本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写&#xff0c;需要的同学可以在这里获取&#xff1a; https://item.taobao.com/item.htm?id728461040949 配套资料获取&#xff1a;https://renesas-docs.100ask.net 瑞萨MCU零基础入门系列教程汇总&#xff1a; ht…

Qt应用开发(基础篇)——普通按钮类 QPushButton QCommandLinkButton

一、前言 QPushButton类继承于QAbstractButton&#xff0c;是一个命令按钮的小部件。 按钮基类 QAbstractButton 按钮或者命令按钮是所有图形界面框架最常见的部件&#xff0c;当按下按钮的时候触发命令、执行某些操作或者回答一个问题&#xff0c;典型的按钮有OK&#xff0c;A…

F. Magic Will Save the World(DP)

Problem - F - Codeforces 黑暗势力的传送门在世界边界打开了&#xff0c;现在整个世界都面临着可怕的威胁。为了关闭传送门并拯救世界&#xff0c;你需要一个接一个地击败n个从传送门中出现的怪物。 只有女巫Vika能够应对这个威胁。她有两个魔法力量——水之魔法和火之魔法。…

Java学习之--类和对象

&#x1f495;粗缯大布裹生涯&#xff0c;腹有诗书气自华&#x1f495; 作者&#xff1a;Mylvzi 文章主要内容&#xff1a;Java学习之--类和对象 类和对象 类的实例化&#xff1a; 1.什么叫做类的实例化 利用类创建一个具体的对象就叫做类的实例化&#xff01; 当我们创建了…