网络与并发编程(一)

news2024/10/9 6:28:28

并发编程介绍_串行_并行_并发的区别

串行、并行与并发的区别

  1. 串行(serial):一个CPU上,按顺序完成多个任务
  2. 并行(parallelism):指的是任务数小于等于cpu核数,即任务真的是一起执行的
  3. 并发(concurrency):一个CPU采用时间片管理方式,交替的处理多个任务。一般是是任务数多余cpu核数,通过操作系统的各种任务调度算法,实现用多个任务“一起”执行(实际上总有一些任务不在执行,因为切换任务的速度相当快,看上去一起执行而已)
  1. 线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位;
  2. 一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线;
  3. 进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段、数据集、堆等)及一些进程级的资源(如打开文件和信号),某进程内的线程在其它进程不可见;
  4. 调度和切换:线程上下文切换比进程上下文切换要快得多。
  • 进程(Process):拥有自己独立的堆和栈,既不共享堆,也不共享栈,进程由操作系统调度;进程切换需要的资源很最大,效率低
  • 线程(Thread):拥有自己独立的栈和共享的堆,共享堆,不共享栈,标准线程由操作系统调度;线程切换需要的资源一般,效率一般(当然了在不考虑GIL的情况下)
  • 协程(coroutine):拥有自己独立的栈和共享的堆,共享堆,不共享栈,协程由程序员在协程的代码里显示调度;协程切换任务资源很小,效率高

进程是什么?

进程(Process)是一个具有一定独立功能的程序关于某个数据集合的一次运行活动

现代操作系统比如Mac OS X,Linux,Windows等,都是支持“多任务”的操作系统,叫“多任务”呢?简单地说,就是操作系统可以同时运行多个任务。打个比方,你一边在用逛淘宝,一边在听音乐,一边在用微信聊天,这就是多任务,至少同时有3个任务正在运行。还有很多任务悄悄地在后台同时运行着,只是桌面上没有显示而已。

对于操作系统来说,一个任务就是一个进程(Process),比如打开一个浏览器就是启动一个浏览器进程,就启动了一个记事本进程,打开两个记事本就启动了两个记事本进程,打开一个Word就启动了一个Word进程。

线程是什么?

线程(Thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。

有些进程还不止同时干一件事,比如微信,它可以同时进行打字聊天,视频聊天,朋友圈等事情。在一个进程内部,要同时干多件事,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”称为线程(Thread)。

并发编程解决方案:

多任务的实现有3种方式:

  1. 多进程模式
  2. 多线程模式
  3. 多进程+多线程模式
  • 启动多个进程,每个进程虽然只有一个线程,但多个进程可以一块执行多个任务
  • 启动一个进程,在一个进程内启动多个线程,这样,多个线程也可以一块执行多个任务
  • 启动多个进程,每个进程再启动多个线程,这样同时执行的任务就更多了,当然这种模型更复杂,实际很少采用。

协程是什么?

协程,Coroutines,也叫作纤程(Fiber),是一种在线程中,比线程更加轻量级的存在,由程序员自己写程序来管理。

当出现IO阻塞时,CPU一直等待IO返回,处于空转状态。这时候用协程,可以执行其他任务。当IO返回结果后,再回来处理数据。充分利用了IO等待的时间,提高了效率。

同步和异步介绍

同步和异步强调的是消息通信机制 (synchronous communication/ asynchronous communication)。

同步(synchronous):A调用B,等待B返回结果后,A继续执行

异步(asynchronous ):A调用B,A继续执行,不等待B返回结果;B有结果了,通知A,A再做处理。

线程_方法包装创建线程

什么是线程

线程(Thread)特点:

  1. 线程(Thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位
  2. 线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位;
  3. 一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线;
  4. 拥有自己独立的栈和共享的堆,共享堆,不共享栈,标准线程由操作系统调度;
  5. 调度和切换:线程上下文切换比进程上下文切换要快得多。

线程的创建方式

Python的标准库提供了两个模块:_threadthreading_thread是低级模块,threading是高级模块,对_thread进行了封装。绝大多数情况下,我们只需要使用threading这个高级模块。

线程的创建可以通过分为两种方式:

1. 方法包装

2. 类包装

线程的执行统一通过start()方法

线程的创建方式(方法包装)

#encoding=utf-8
#方法方式创建线程
from threading import Thread
from time import sleep
def func1(name):
  for i in range(3):
    print(f"thread:{name} :{i}")
    sleep(1)
​
if __name__ == '__main__':
  print("主线程,start")
  #创建线程
  t1 = Thread(target=func1,args=("t1",))
  t2 = Thread(target=func1,args=("t2",))
  #启动线程
  t1.start()
  t2.start()
  print("主线程,end")
​
'''
运行结果可能会出现换行问题,是因为多个线程抢夺控制台输出的IO流。
比如,如下的输出换行就没有按照预想的显示:
​
主线程,start
thread:t1 :0
thread:t2 :0
主线程,end
thread:t2 :1thread:t1 :1
​
thread:t2 :2
thread:t1 :2
'''

线程的创建方式(类包装)

#encoding=utf-8
#类的方式创建线程
from threading import Thread
from time import sleep
​
class MyThread(Thread):
  def __init__(self,name):
    Thread.__init__(self)
    self.name =name
  def run(self):
    for i in range(3):
      print(f"thread:{self.name} :{i}")
      sleep(1)
​
if __name__ == '__main__':
  print("主线程,start")
  #创建线程(类的方式)
  t1 = MyThread('t1')
  t2 = MyThread('t2')
  #启动线程
  t1.start()
  t2.start()
  print("主线程,end")

线程_join()和守护线程

join()

之前的代码,主线程不会等待子线程结束。

如果需要等待子线程结束后,再结束主线程,可使用join()方法。

#encoding=utf-8
from threading import Thread
from time import sleep
def func1(name):
  for i in range(3):
    print(f"thread:{name} :{i}")
    sleep(1)
​
if __name__ == '__main__':
  print("主线程,start")
  #创建线程
  t1 = Thread(target=func1,args=("t1",))
  t2 = Thread(target=func1,args=("t2",))
  #启动线程
  t1.start()
  t2.start()
  #主线程会等待t1,t2结束后,再往下执行
  t1.join()
  t2.join()
  print("主线程,end")

守护线程

在行为上还有一种叫守护线程,主要的特征是它的生命周期。主线程死亡,它也就随之死亡。在python中,线程通过setDaemon(True|False)来设置是否为守护线程。

守护线程的作用:

  • 守护线程作用是为其他线程提供便利服务,守护线程最典型的应用就是 GC (垃圾收集器)。
#encoding=utf-8
from threading import Thread
from time import sleep
​
class MyThread(Thread):
  def __init__(self,name):
    Thread.__init__(self)
    self.name =name
  def run(self):
    for i in range(3):
      print(f"thread:{self.name} :{i}")
      sleep(1)
​
if __name__ == '__main__':
  print("主线程,start")
  #创建线程(类的方式)
  t1 = MyThread('t1')
  #t1设置为守护线程
  t1.setDaemon(True)#3.10后被废弃,可以直接:t1.daemon=True
  #启动线程
  t1.start()
  print("主线程,end")
​

线程_全局解释器锁GIL问题

全局锁GIL问题

在python中,无论你有多少核,在Cpython解释器中永远都是假象。无论你是4核,8核,还是16核.......不好意思,同一时间执行的线程只有一个线程,它就是这个样子的。这个是python的一个开发时候,设计的一个缺陷,所以说python中的线程是“含有水分的线程”。

Python GIL(Global Interpreter Lock)

Python代码的执行由Python 虚拟机(也叫解释器主循环,CPython版本)来控制,Python 在设计之初就考虑到要在解释器的主循环中,同时只有一个线程在执行,即在任意时刻,只有一个线程在解释器中运行。对Python 虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。

image-20211204114226986

⚠️GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念,同样一段代码可以通过CPython,PyPy,Psyco等不同的Python执行环境来执行,就没有GIL的问题。然而因为CPython是大部分环境下默认的Python执行环境。所以在很多人的概念里CPython就是Python,也就想当然的把GIL归结为Python语言的缺陷

线程_线程同步和互斥锁_资源

线程同步和互斥锁

线程同步的概念

处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象。 这时候,我们就需要用到“线程同步”。 线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕后,下一个线程再使用。

【示例】多线程操作同一个对象(未使用线程同步)

#encoding=utf-8
from threading import Thread
from time import sleep
​
class Account:
  def __init__(self,money,name):
    self.money = money
    self.name = name
​
​
#模拟提款操作
class Drawing(Thread):
  def __init__(self,drawingNum,account):
    Thread.__init__(self)
    self.drawingNum = drawingNum
    self.account = account
    self.expenseTotal = 0
  def run(self):
    if self.account.money-self.drawingNum<0:
      return
    sleep(1) #判断完后阻塞。其他线程开始运行。
    self.account.money -= self.drawingNum;
    self.expenseTotal += self.drawingNum;
    print(f"账户:{self.account.name},余额是:{self.account.money}")
    print(f"账户:{self.account.name},总共取了:{self.expenseTotal}")
​
if __name__ == '__main__':
  a1 = Account(100,"gaoqi")
  draw1 = Drawing(80,a1) #定义取钱线程对象;
  draw2 = Drawing(80,a1) #定义取钱线程对象;
  draw1.start()  #你取钱
  draw2.start()  #你老婆取钱

没有线程同步机制,两个线程同时操作同一个账户对象,竟然从只有100元的账户,轻松取出80*2=160元,账户余额竟然成为了-60。这么大的问题,显然银行不会答应的。

我们可以通过“锁机制”来实现线程同步问题,锁机制有如下几个要点:

  1. 必须使用同一个锁对象
  2. 互斥锁的作用就是保证同一时刻只能有一个线程去操作共享数据,保证共享数据不会出现错误问题
  3. 使用互斥锁的好处确保某段关键代码只能由一个线程从头到尾完整地去执行
  4. 使用互斥锁会影响代码的执行效率
  5. 同时持有多把锁,容易出现死锁的情况

互斥锁是什么?

互斥锁: 对共享数据进行锁定,保证同一时刻只能有一个线程去操作。

注意: 互斥锁是多个线程一起去抢,抢到锁的线程先执行,没有抢到锁的线程需要等待,等互斥锁使用完释放后,其它等待的线程再去抢这个锁。

threading模块中定义了Lock变量,这个变量本质上是一个函数,通过调用这个函数可以获取一把互斥锁。

【示例】多线程操作同一个对象(增加互斥锁,使用线程同步)

#encoding=utf-8
from threading import Thread,Lock
from time import sleep
​
class Account:
  def __init__(self,money,name):
    self.money = money
    self.name = name
​
​
#模拟提款操作
class Drawing(Thread):
  def __init__(self,drawingNum,account):
    Thread.__init__(self)
    self.drawingNum = drawingNum
    self.account = account
    self.expenseTotal = 0
  def run(self):
    lock1.acquire() 
    if self.account.money-self.drawingNum<0:
      return
    sleep(1) #判断完后阻塞。其他线程开始运行。
    self.account.money -= self.drawingNum;
    self.expenseTotal += self.drawingNum;
    lock1.release()
    print(f"账户:{self.account.name},余额是:{self.account.money}")
    print(f"账户:{self.account.name},总共取了:{self.expenseTotal}")
​
if __name__ == '__main__':
  a1 = Account(100,"gaoqi")
  lock1 = Lock()
  draw1 = Drawing(80,a1) #定义取钱线程对象;
  draw2 = Drawing(80,a1) #定义取钱线程对象;
  draw1.start()  #你取钱
  draw2.start()  #你老婆取钱
  • acquirerelease方法之间的代码同一时刻只能有一个线程去操作
  • 如果在调用acquire方法的时候 其他线程已经使用了这个互斥锁,那么此时acquire方法会堵塞,直到这个互斥锁释放后才能再次上锁。

线程_死锁问题和解决方案

死锁

在多线程程序中,死锁问题很大一部分是由于一个线程同时获取多个锁造成的。

from threading import Thread, Lock
from time import sleep
​
def fun1():
  lock1.acquire()
  print('fun1拿到菜刀')
  sleep(2)
  lock2.acquire()
  print('fun1拿到锅')
​
  lock2.release()
  print('fun1释放锅')
  lock1.release()
  print('fun1释放菜刀')
​
​
def fun2():
  lock2.acquire()
  print('fun2拿到锅')
  lock1.acquire()
  print('fun2拿到菜刀')
  lock1.release()
  print('fun2释放菜刀')
  lock2.release()
  print('fun2释放锅')
​
​
if __name__ == '__main__':
  lock1 = Lock()
  lock2 = Lock()
​
  t1 = Thread(target=fun1)
  t2 = Thread(target=fun2)
  t1.start()
  t2.start()

死锁的解决方法

死锁是由于“同步块需要同时持有多个锁造成”的,要解决这个问题,思路很简单,就是:同一个代码块,不要同时持有两个对象锁。

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

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

相关文章

Java 变得越来越像 Rust?

随着编程技术的增强和复杂性的提升&#xff0c;许多编程语言也纷纷效仿&#xff0c;Java 也不例外。 另一边&#xff0c;尽管社区内部问题重重&#xff0c;但 Rust 仍逐年获得开发人员的喜爱。这背后都是有原因的&#xff1a;Rust 的编译器让开发人员避免了各种问题。编译器对…

Spring的BeanFactory和FactoryBean有什么区别?

两者的区别 BeanFactory定义了ioc容器的最基本形式,并提供了ioc容器应遵守的的最基本的接口,也就是Spring ioc所遵守的最底层和最基本的编程规范,它只是个接口,并不是ioc容器的具体实现。它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。再来说说…

jvisualvm 使用教程

之前看过 jvisualvm&#xff0c;但是那个时候对 JVM 并不是很熟悉&#xff0c;后面看了下八股文&#xff0c;看了下 JVM 的相关知识之后&#xff0c;发现多了解点 JVM 的东西&#xff0c;对我们 CRUD 其实是有指导意义的&#xff0c;就比如我们通常会 new 一堆的没有用到的对象…

Vue项目+ 打包解决静态资源无法加载和路由加载无效(404)问题

vue项目npm run build打包发到服务器上&#xff0c;背景图片消失 问题 登录页背景图片丢失 控制台报错 找到我们的 config文件夹下面的 index.js 配置文件找到其中的 build 相关配置&#xff0c;assetsPublicPath 这一项默认配置的是‘/’ 我们将他改成 ‘./’ 修改后 修…

【数据结构】初识数据结构与复杂度总结

前言 C语言这块算是总结完了&#xff0c;那从本篇开始就是步入一个新的大章——数据结构&#xff0c;这篇我们先来认识一下数据结构有关知识&#xff0c;以及复杂度的相关知识 个人主页&#xff1a;小张同学zkf 若有问题 评论区见 感兴趣就关注一下吧 目录 1.什么是数据结构 2.…

k8s 部署 canal 集群,RocketMQ 模式

k8s 部署 canal 集群&#xff0c;RocketMQ 模式 k8s 部署 canal 集群&#xff0c;RocketMQ 模式前提MySQLRocketMQ制作 canal-admin、canal-server 镜像 部署 zookeeper部署 canal-admin部署 canal-server测试 k8s 部署 canal 集群&#xff0c;RocketMQ 模式 前提 MySQL 开启…

Excel制作甘特图

使用Excel表格制作甘特图&#xff0c;可根据任务开始时间和结束时间自动计算工时&#xff0c;并自动用指定颜色填充横道图。 1.新建Excel文档&#xff0c;先设置项目基本信息&#xff0c;包括表格名称&#xff0c;这里设置为“**项目甘特图”&#xff1b;然后添加任务序号列&a…

移动端WEB开发之响应式布局

一、响应式开发 1.1 响应式开发原理 就是使用媒体查询针对不同宽度的设备进行布局和样式的设置&#xff0c;从而适配不同设备的目的。 1.2 响应式布局容器 响应式需要一个父级做为布局容器&#xff0c;来配合子级元素来实现变化效果。原理就是在不同屏幕下&#xff0c;通过媒体…

机器狗首次阵亡!美国警方披露详情

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 新建了免费的人工智能中文站https://ai.weoknow.com 新建了收费的人工智能中文站https://ai.hzytsoft.cn/ 更多资源欢迎关注 那天&#xff0c;唯一的伤亡者是我们的机器狗。 美国警察最新公布一则案件&#xff1a;波士顿…

python set是什么类型

python set是一种数据类型&#xff0c;数学里的集合概念&#xff0c;在Python语言里对应的是set类型。与list&#xff0c;tuple不同的地方是&#xff0c;set更加强调的是一种“从属关系”&#xff08;membership&#xff09;&#xff0c;跟顺序无关&#xff0c;所以有重复的元素…

达梦数据库 索引管理

索引的基本认识 索引是为了快速检索和定位数据行而创建的一种数据结构。索引是由表中索引列数据进行排序后的集合和指向这些值的物理标识&#xff08;例如&#xff1a;ROWID 等聚集索引键&#xff09;共同组成。在 DM 中&#xff0c;除了位图索引、位图连接索引、全文索引和空…

代码随想录算法训练营第二十九天(回溯5)|491. 非递减子序列、46. 全排列、47. 全排列 II(JAVA)

文章目录 491. 非递减子序列解题思路源码 46. 全排列解题思路源码 47. 全排列 II解题思路源码 总结 491. 非递减子序列 给你一个整数数组 nums &#xff0c;找出并返回所有该数组中不同的递增子序列&#xff0c;递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。 …

练手项目层初阶1—《详解静态版本——通讯录管理系统》

文章目录 &#x1f6a9;前言&#x1f50a; 项目需求&#x1f4da; 项目知识点包含&#x1f9e9;项目框架&#x1f4dd;框架拆解分析✨Struct_book.h 头文件解析✨Struct_book.c文件解析✨test_book.c文件解析 &#x1f4fa;演示效果&#x1f680;完整代码 &#x1f6a9;前言 俗…

Linux下Qt生成程序崩溃文件

文章目录 1.背景2.Qt编译生成程序2.1.profile模式的本质 3.执行程序&#xff0c;得到core文件4.代码定位4.1.直接使用gdb4.2.使用QtCreator 5.总结6.题外话6.1.profile模式和debug模式的区别 1.背景 在使用Qt时&#xff0c;假如在windows&#xff0c;当软件崩溃时&#xff0c;…

Linux 服务器间SSH免密码登录与拷贝文件(SCP)

适用背景 工作中频繁登录服务器和拷贝文件&#xff0c;都会提示输入密码在持续集成的场景下&#xff0c;自动部署应用时是没有人工干预的 解决方案 下面以实现A服务器到B服务器的免密码登录和拷贝文件为例&#xff0c;介绍相关的配置。&#xff08;即A访问B不需要输密码&…

不能在主机和虚拟机之间拷贝文本(虚拟机ubuntu16.04)

问题 ubuntu16.04不能在主机和虚拟机之间拷贝文本。 原因 vmware tools没安装好。 解决办法 重新安装vmware tools&#xff0c;步骤入下&#xff1a; 让虚拟机加载C:\Program Files (x86)\VMware\VMware Workstation\linux.iso光盘文件&#xff0c;设置如下&#xff1a; …

DHT11温湿度传感器使用视频教程分享

下载地址&#xff1a; 温湿度计(STCDHT11): https://url83.ctfile.com/d/45573183-60623983-9b7f6c?p7526 (访问密码: 7526)

使用 CloudDM 操作 PostgrgSQL 数据库

CloudDM 简介 CloudDM 是 ClouGence 公司推出的一款一站式数据库管理工具&#xff0c;使用它可以方便地访问和管理 MySQL、Oracle、PostgreSQL、阿里云 RDS、Greenplum、TiDB、Redis、StarRocks、Doris、SelectDB、SQL SERVER、ClickHouse、OceanBase 、PolarDB-X 、IBM Db2 等…

JVM之常用监控工具

JVM之常用监控工具 jps jinfo 获取配置信息 基本语法 jinfo [options] <pid>常用选项 -sysprops&#xff1a;显示JVM进程的系统属性。-flags&#xff1a;显示用于启动JVM的命令行标志和VM选项。-flag <name>&#xff1a;显示指定JVM标志的当前值。-flag [|-]&…

vue想要突破全局样式限制又不影响别的页面样式怎么办

<!-- 用scope盖不住全局&#xff0c;随意来个class匹配私定&#xff0c;搜索关键词&#xff1a;不要随便改&#xff0c;乱打class名 --> <style> .lkajsdfjkalsfhkljashkflhaskl .el-input.el-input--default.el-input--suffix { width: 160px !important; } …