并发编程 - 总结

并发编程

核心: 并发,进程,线程,协程

并发与并行

并发:是伪并行,即看起来是同时运行。单个cpu+多道技术就可以实现并发并行:同时运行,只有具备多个cpu才能实现并行补充: 多道技术实现了单核下实现并发

同步 、异步 | 阻塞、非阻塞

同步 、异步: 是指任务提交的方式     同步:提交任务后原地等待任务执行完毕,拿到任务的返回值才能继续下一行代码,导致程序串行    异步:提交任务后不在原地等待,任务一旦执行完毕就会触发回调函数的执行,程序是并发阻塞 、非阻塞 : 指程序执行中的运行状态     阻塞: 出现io 非阻塞: 没有出现 io    异步非阻塞: 程序在执行过程中没有出现io,任务提交是异步,无需等待

进程

进程的概念

1.进程是指运行的应用程序,内存空间,操作系统的调度称为一个进程2.进程是竞争计算机系统有限资源的基本单位,也是进行处理机调度的基本单位3.进程是程序的基本执行实体注: 进程与进程的空间是物理隔离的

创建进程 multiprocess process

#当前文件名称为test.py
from multiprocessing import Process
?
def func():
    print(12345)
?
if __name__ == ‘__main__‘: #windows 下才需要写这个,这和系统创建进程的机制有关系,不用深究,记着windows下要写就好啦
#首先我运行当前这个test.py文件,运行这个文件的程序,那么就产生了进程,这个进程我们称为主进程
    p = Process(target=func,) #将函数注册到一个进程中,p是一个进程对象,此时还没有启动进程,只是创建了一个进程对象。并且func是不加括号的,因为加上括号这个函数就直接运行了对吧。
    p.start() #告诉操作系统,给我开启一个进程,func这个函数就被我们新开的这个进程执行了,而这个进程是我主进程运行过程中创建出来的,所以称这个新创建的进程为主进程的子进程,而主进程又可以称为这个新进程的父进程。
#而这个子进程中执行的程序,相当于将现在这个test.py文件中的程序copy到一个你看不到的python文件中去执行了,就相当于当前这个文件,被另外一个py文件import过去并执行了。
#start并不是直接就去执行了,我们知道进程有三个状态,进程会进入进程的三个状态,就绪,(被调度,也就是时间片切换到它的时候)执行,阻塞,并且在这个三个状态之间不断的转换,等待cpu执行时间片到了。
    print(‘*‘ * 10) #这是主进程的程序,上面开启的子进程的程序是和主进程的程序同时运行的,我们称为异步
?
2.进程同步部分
进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的,而共享带来的是竞争,竞争带来的结果就是错乱,如何控制,就是加锁处理。
3. 进程池部分
1.进程池的概念
def __init__(self,person):
    super().__init__()
self.person=person
def run(self):
    print(os.getpid())
    print(self.pid)
    print(self.pid)
    print(‘%s 正在和女主播聊天‘ %self.person)
def start(self):
    #如果你非要写一个start方法,可以这样写,并且在run方法前后,可以写一些其他的逻辑
    self.run()
if __name__ == ‘__main__‘:
    p1=MyProcess(‘Jedan‘)
    p2=MyProcess(‘太白‘)
    p3=MyProcess(‘alDSB‘)
    p1.start() #start内部会自动调用run方法
    p2.start()
    # p2.run()
    p3.start()
    p1.join()
    p2.join()
    p3.join()

p. join() 主进程等待子进程运行完再运行

注意: if __name__ == ‘__main__‘:下发的代码块都是主进程的代码

僵尸进程

1.子进程运行结束,但占用的空间没有让出来("死了,没死干净")2.所有的子进程都会经历僵尸进程的阶段3.有害:占用资源4.僵尸进程是正常的运行过程,目的是为了让父进程查看子进程的状态5.父进程结束前会将子进程占用的资源回收

孤儿进程

1.父进程结束,子进程没有结束2.无害

守护进程 p.daem = True

1.当子进程执行的任务在父进程代码运行完毕后就没有存在的必要了,那么该子进程就应该设置为守护进程2.被守护的子进程应该在p.start()之前设置3.被守护的子进程内不能再开启子进程体现为:主进程代码运行结束,子进程就结束

互斥锁 Lock

进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的,而共享带来的是竞争,竞争带来的结果就是错乱,如何控制,就是加锁处理

1.锁将并发变成了串行,牺牲了运行效率,但避免了竞争2.锁在主进程中实例化产生,在子进程中使用3.在修改数据处加锁,修改不同的数据用不同的锁
from multiprocessing import Process,Lock
import os,time
def work(n,lock):
#加锁,保证每次只有一个进程在执行锁里面的程序,这一段程序对于所有写上这个锁的进程,大家都变成了串行
    lock.acquire()
    print(‘%s: %s is running‘ %(n,os.getpid()))
    time.sleep(1)
    print(‘%s:%s is done‘ %(n,os.getpid()))
    #解锁,解锁之后其他进程才能去执行自己的程序
    lock.release()
if __name__ == ‘__main__‘:
    lock=Lock()
    for i in range(5):
        p=Process(target=work,args=(i,lock))
        p.start()

进程间通信

1.文件的方式 速度慢,要自己加锁,容易导致死锁2.队列Queue的方式(IPC) 管道+锁,不需要自己处理锁

生产者消费者模型

在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。

线程

进程是资源单位线程是执行单位同一进程下的线程资源共享,这些资源源自进程的空间线程开销远远小于进程,且开线程的速度是开进程的百倍# 线程没有父子之分 主线程的生命周期就代表着进程的生命周期(可以理解为主线程就代表着进程)

创建线程 threading Thread

from threading import Thread
import time
def sayhi(name):
    time.sleep(2)
    print(‘%s say hello‘ %name)
if __name__ == ‘__main__‘:
    t=Thread(target=sayhi,args=(‘太白‘,))
    t.start()
    print(‘主线程‘)

守护线程

因为线程的运行需要进程中的资源,而主线程又代表着进程,守护线程会随着主线程的结束跟着结束,所以守护线程是主线程等到非守护线程的结束再随着主线程结束

Event事件

一个线程要等到另一个线程运行到某一时刻再运行

event.isSet():返回event的状态值;
event.wait():如果 event.isSet()==False将阻塞线程;
event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;
event.clear():恢复event的状态值为False。

GIL

同一个进程下开启的多线程,同一时间只能有一个在运行,因为线程之间共用一套资源GIL锁的存在是因为垃圾回收机制也是一个进程中的线程,为了保证数据安全GIL锁导致了python多线程无法利用多核优势IO密集型?计算密集型?

递归锁 RLock

递归锁每acquire一次身上的计数就加一,只要身上的计数不为0,其他人就抢不到

信号量 Semaphore

也是一种锁,不同于互斥锁,信号量是一把锁有多把钥匙

池 concurrent

concurrent.futures模块提供了高度封装的异步调用接口ThreadPoolExecutor:线程池,提供异步调用ProcessPoolExecutor: 进程池,提供异步调用
#submit(fn, *args, **kwargs)
异步提交任务
#map(func, *iterables, timeout=None, chunksize=1)
取代for循环submit的操作
?
#shutdown(wait=True)
相当于进程池的pool.close()+pool.join()操作
wait=True,等待池内所有任务执行完毕回收完资源后才继续
wait=False,立即返回,并不会等待池内的任务执行完毕
但不管wait参数为何值,整个程序都会等到所有任务执行完毕
submit和map必须在shutdown之前
?
#result(timeout=None)
取得结果
?
#add_done_callback(fn)
回调函数

协程

协程:是单线程下的并发,又称微线程,纤程。

协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。

1. python的线程属于内核级别的,即由操作系统控制调度(如单线程遇到io或执行时间过长就会被迫交出cpu执行权限,切换其他线程运行)2. 单线程内开启协程,一旦遇到io,就会从应用程序级别(而非操作系统)控制切换,以此来提升效率(!!!非io操作的切换与效率无关)

优点

1. 协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级2. 单线程内就可以实现并发的效果,最大限度地利用cpu

缺点

1. 协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程2. 协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程

原文地址:https://www.cnblogs.com/waller/p/12079505.html

时间: 2024-10-05 23:25:09

并发编程 - 总结的相关文章

Java并发编程:Concurrent锁机制解析

.title { text-align: center } .todo { font-family: monospace; color: red } .done { color: green } .tag { background-color: #eee; font-family: monospace; padding: 2px; font-size: 80%; font-weight: normal } .timestamp { color: #bebebe } .timestamp-kwd

VS C++ 并发编程

1.VS2012及以上版本,支持C++11 thread类的并发编程. 相关材料可以参考博客:http://www.cnblogs.com/rangozhang/p/4468754.html 2.但对其之前的版本,可采用以下方式,实现类成员函数创建子线程实现并发. 首先需实现线程类的run函数,故定义了线程类的头文件和其对应的函数实现,具体如图1,2所示: 图1 线程类的头文件 图2 线程类的实现文件 注意到继承的DerivedThread类,只需将并发执行的函数写在其对应的run()函数内即可

Java并发编程:Callable、Future和FutureTask(转)

Java并发编程:Callable.Future和FutureTask 在前面的文章中我们讲述了创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果. 如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦. 而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果. 今天我们就来讨论一下Callabl

[笔记][Java7并发编程实战手册]3.2 资源的并发访问控制Semaphore信号量

[笔记][Java7并发编程实战手册]系列目录 简介 本文学习信号量Semaphore机制. Semaphore 本质是一个共享锁 内部维护一个可用的信号集,获取信号量之前需要先申请获取信号数量:用完之后,则需要释放信号量:如果不释放,那么其他等待线程则一直阻塞直到获取信号量或则被中断为止 本人的理解是:互斥锁是同一时间只能一个线程访问,而在这里,是同一时间允许获取到了信号量的线程并发访问,而没有获取到信号量的则必须等待信号量的释放: 将信号量初始化为 1,使得它在使用时最多只有一个可用的许可,

【Java并发编程】之七:使用synchronized获取互斥锁的几点说明

 在并发编程中,多线程同时并发访问的资源叫做临界资源,当多个线程同时访问对象并要求操作相同资源时,分割了原子操作就有可能出现数据的不一致或数据不完整的情况,为避免这种情况的发生,我们会采取同步机制,以确保在某一时刻,方法内只允许有一个线程. 采用synchronized修饰符实现的同步机制叫做互斥锁机制,它所获得的锁叫做互斥锁.每个对象都有一个monitor(锁标记),当线程拥有这个锁标记时才能访问这个资源,没有锁标记便进入锁池.任何一个对象系统都会为其创建一个互斥锁,这个锁是为了分配给线程的,

Java 并发编程之图形界面应用程序及死锁问题

不知道为什么这本书还要讲一个界面应用程序,Java的界面做的很糟糕,效率低下,而且界面是java的弱项,可能是因为这里边是有一些并发编程的知识吧. 为什么GUI是单线程的 无论是Swing还是AWT都是单线程的.但它不仅限于在java中,在Qt,NexiStep,macOs CoCoa X windows以及其它环境中的GUI框架都是单线程的,许多人都曾经尝试编写多线程的GUI框架,但最终都由于竞态条件和死锁导致的稳定性问题而又重新回到单线程的事件队列模型:采用一个专门的线程从队列中抽取事件,并

并发编程基础之生产者消费者模式

一:概念 生产者消费者模式是java并发编程中很经典的并发情况,首先有一个大的容器,生产者put元素到 容器中,消费者take元素出来,如果元素的数量超过容器的容量时,生产者不能再往容器中put元素 ,处于阻塞状态,如果元素的数量等于0,则消费者不能在从容器中take数据,处于阻塞状态. 二:示例 /** * */ package com.hlcui.main; import java.util.LinkedList; import java.util.concurrent.ExecutorSe

Java并发编程 Volatile关键字解析

volatile关键字的两层语义 一旦一个共享变量(类的成员变量.类的静态成员变量)被volatile修饰之后,那么就具备了两层语义: 1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的. 2)禁止进行指令重排序. 根据volatile的语义,我们可以看到,volatile主要针对的是并发三要素(原子性,可见性和有序性)中的后两者有实际优化作用. 可见性: 线程本身并不直接与主内存进行数据的交互,而是通过线程的工作内存来完成相应的操作.

Python 3 并发编程多进程之队列(推荐使用)

Python 3 并发编程多进程之队列(推荐使用) 进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的. 可以往队列里放任意类型的数据 创建队列的类(底层就是以管道和锁定的方式实现): 1 Queue([maxsize]):创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递. 参数介绍: 1 maxsize是队列中允许最大项数,省略则无大小限制. 方法介绍: 1.主要

Java并发编程:volatile关键字解析(转)

volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字才得以重获生机. volatile关键字虽然从字面上理解起来比较简单,但是要用好不是一件容易的事情.由于volatile关键字是与Java的内存模型有关的,因此在讲述volatile关键之前,我们先来了解一下与内存模型相关的概念和知识,然后分析了volatile关键字的实现原理,最后给出了几个使用vola