python语法基础-并发编程-线程-长期维护

###############   线程和GIL,全局解释器锁    ##############

"""
线程

为什么会有进程?
主要是能够同时处理多个任务,多个任务还要进行切换,时间片轮转
为什么会有线程?
进程并不是执行任务的最小单元,每一个进程里面有都一个线程,我们叫做主线程,
早期没有线程,一个进程只能干一个任务,如果有多个任务,只能多起进程,进程太多不行的,
进程内部的内存是共享的,所以需要线程,否则还要涉及到进程间的通信,这比较浪费资源

所以线程的出现解决了两个问题:
1,进程内部的通信
2,一个进程可以处理多个任务,

线程的开销比进程少,可以认为是一个轻型的进程,
进程可以任务是车间,线程可以认为是每一个工人,

进程是资源分配的最小单位,线程是cpu调度的最小单位,

###########################
进程和线程的区别:
1,进程之间的内存是独立的,但是一个进程之内的线程是可以共享的,
2,进程之间切换是慢于线程之间的切换的,

"""

# 第一个例子
from threading import Thread
import time,os
# 多线程并发
# def func(n):  # 这是子线程完成的
#     time.sleep(1)  # 虽然是打印了10次,但是只等待了1秒,所以10线程之间是并发的,
#     print(n)
#
# for i in range(10):  # 启动10个线程,
#     t = Thread(target=func,args=(1,))  # 注册
#     t.start()  # 这是启动了一个线程

# 第二种启动线程的方法:
# class MyTread(Thread):
#
#     def __init__(self,arg):
#         super().__init__()
#         self.arg=arg
#     def run(self):
#         time.sleep(1)
#         print(1,os.getpid())
#
#
# for i in range(10):
#     t = MyTread(10)  # 传递参数
#     t.start()
#
# print("主线程",os.getpid())  # 打印子进程和主进程的进程号,都是一样的

# 进程里面存了导入的模块,文件所在的位置,内置的函数代码
# 线程里面存了少量的必不可少代码,比如做加法运算,需要的参数,

# 所以目前看起来进程和线程的启动是非常的类似的,
#########################################
# 同一个数据,多个线程去操作,也会出现问题,所以也有线程锁的概念,
# 这种机制叫做全局解释器锁,英文GIL,
# 这种机制的结果就是:同一时间只有一个线程去访问cpu,
# 你想要访问数据,就必须拿到这个钥匙,
# 这个锁,所的是线程,不是锁的某一个数据,
# 这样不好,因为同一时间cpu上面只有一个线程,这样不能充分的利用cpu
# 这不是Python语言的问题,这是cPython解释器的问题,如果你有一个jPython解释器就行

# 这的确是一个弊病,导致Python的多线程形同虚设,
# 那么为什么不解决呢?
# java和c++都是编译性语言,Python是一个解释性语言,php也是,
# 目前解释性语言就是存在这个问题,这个矛盾还不可调和,

# 是否把数据加锁就可以了,也是不行的,数据太多了,这么大范围的加锁,最终导致的效率,还不如全局解释器锁的性能好,

# 在cPython解释器下的Python程序,在同一时间,多个线程只能有一个线程在cpu执行,
# 这不意味着多线程就没有意义了,
# 因为只有涉及到计算的地方才会使用到CPU,
# 高CPU:所以在计算类的高cpu利用率的,Python不占优势,
# 高IO:我们写的代码很多都涉及到这种,
# 比如qq聊天,处理日志文件,爬取网页,处理web请求,读写数据库,都是高io的,都是有Python用武之地的,
# 所以Python不能处理计算类高的问题,这不影响他在web编程的作用,

# 如果你真的需要高并发呢,你可以使用多进程,就不会有GIL锁了,

#######################################
# Threading中的其他方法
import threading
def func(n):
    print(n,threading.current_thread())  # <Thread(Thread-1, started 5428)>

for i in range(10):
    threading.Thread(target=func,args=(1,)).start()

print(threading.active_count())  # 查看活跃的线程数,
print(threading.current_thread())

###############    守护线程    ##############

# 守护线程:

from threading import Thread
import time
def func1(name):
    while True:
        print(11111111)
        time.sleep(1)

def func2(name):
    print(2222222)
    time.sleep(5)

if __name__ == ‘__main__‘:
    t=Thread(target=func1,args=(‘andy‘,))
    t.daemon = True  # 主线程代码结束,子线程随之结束,
    # 不加守护线程,主线程就会等待子线程的结束,然后主线程才会结束,
    t.start()

    t2=Thread(target=func2,args=(‘lucy‘,))
    t2.start()
    # 主线程会等待子线程结束
    print(‘主线程‘)
    print(t.is_alive())
    ‘‘‘
    主线程
    True
    ‘‘‘

# 所以进程的守护进程,和线程的守护进程是有不一样的点的
# 1.对主进程来说,运行完毕指的是主进程代码运行完毕
# 2.对主线程来说,运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕,主线程才算运行完毕
# 这是一个盲点,后续需要再看看,

###############    线程锁---互斥锁,死锁,递归锁,    ##############

# 线程锁
# 你用进程锁的时候不多,线程锁用的多
# 为什么会用到线程锁?
# 就是因为多个线程同时操作一个资源会出问题,

# 一般的锁,lock,叫做互斥锁,就是说你只能拿到你把钥匙,
# 死锁
# 递归锁,你拿到一把钥匙,就拿到了这个钥匙串,别的线程就拿到不到这个钥匙串了,

from threading import Lock,Thread
import time

# 模拟多线程出错的场景,
# 全局的n是10,然后创建10个线程,去执行n-1的操作,
# 结果按理说应该是0,但是结果是9,
# 为什么?
# 10个线程去拿n=10,所有人拿的都是10,然后都同步减去1,然后返回都是9,所以是9,
# 但是有GIL锁啊,为什么还会产生这种情况?
# 还是因为时间片轮转,你取到了数据,但是还没有来得及减1,这个值就被其他的线程拿走了,还是10,

#
# def func():
#     global n
#     temp = n
#     time.sleep(0.2)
#     n= temp-1
#
# n = 10
# t_list=[]
# for i in range(10):
#     t= Thread(target=func)
#     t.start()
#     t_list.append(t)
#
#
# for i in t_list:t.join()
# print(n)

# 模拟线程加锁的情况,然后看看结果
# 这样加了锁之后,结果应该就是0了,是的,结果是0了
# 加锁降低了性能,但是保证了数据的安全性,

# def func(lock):
#     global n
#     lock.acquire()
#     temp = n
#     time.sleep(0.2)
#     n= temp-1
#     lock.release()
#
# n = 10
# t_list=[]
# lock = Lock()
#
# for i in range(10):
#     t= Thread(target=func,args=(lock,))
#     t.start()
#     t_list.append(t)
#
#
# for i in t_list:t.join()
# print(n)

# 现在看看科学家吃面的问题,看看死锁的场景
# 这个例子是只有拿到面和叉子,才可以吃面,
#
#
# noodle_lock = Lock()
# fork_lock = Lock()
# def eat1(name):
#     noodle_lock.acquire()
#     print("%s拿到面条"%name)
#     fork_lock.acquire()
#     print("%s拿到叉子"%name)
#     print("吃面")
#     fork_lock.release()
#     noodle_lock.release()
#
#
#
# def eat2(name):
#     fork_lock.acquire()
#     print("%s拿到叉子"%name)
#     time.sleep(1)
#     noodle_lock.acquire()
#     print("%s拿到面条"%name)
#     print("吃面")
#     noodle_lock.release()
#     fork_lock.release()
#
# Thread(target=eat1,args=("name1",)).start()
# Thread(target=eat2,args=("name2",)).start()
# Thread(target=eat1,args=("name3",)).start()
# Thread(target=eat1,args=("name4",)).start()
#
# """
# name1拿到面条
# name1拿到叉子
# 吃面
# name2拿到叉子
# name3拿到面条
# 这种情况不同的人拿到了叉子和面条,就无法进行了,就阻塞了,就死锁了,
# 怎么解决?有一个递归锁
# """

# 递归锁,来解决死锁的问题
# # 你可以看做是钥匙串上面的两把钥匙,一旦你拿到了一把钥匙,就证明你拿到了整个钥匙串了,
from threading import RLock

noodle_lock = fork_lock = RLock()  # 你可以看做是钥匙串上面的两把钥匙
def eat1(name):
    noodle_lock.acquire()
    print("%s拿到面条"%name)
    fork_lock.acquire()
    print("%s拿到叉子"%name)
    print("吃面")
    fork_lock.release()
    noodle_lock.release()

def eat2(name):
    fork_lock.acquire()
    print("%s拿到叉子"%name)
    time.sleep(1)
    noodle_lock.acquire()
    print("%s拿到面条"%name)
    print("吃面")
    noodle_lock.release()
    fork_lock.release()

Thread(target=eat1,args=("name1",)).start()
Thread(target=eat2,args=("name2",)).start()
Thread(target=eat1,args=("name3",)).start()
Thread(target=eat2,args=("name4",)).start()

###############   信号量和事件   ##############

# 信号量
# 信号量就是控制只能有n个线程能访问这段代码

# from threading import Semaphore,Thread
# import time
# def func(sem,a,b):
#     sem.acquire()
#     time.sleep(1)
#     print(a+b)
#     sem.release()
#
# sem = Semaphore(4)
# for i in range(10):
#     t = Thread(target=func,args=(sem,i,i+5))
#     t.start()

# 事件:
# 事件被创建的时候是false状态,这个false状态会导致wait被阻塞,
# true状态的时候,wait就不阻塞了
# clear,设置为false
# set,设置状态未false,
# 上次举例的是红绿灯的例子,

# 现在我们举一个例子,检测数据库的可连接情况
# 启动两个线程,
# 第一个线程连接数据库,
# 等待一个信号,告诉我们之间的网络是通的
# 第二个线程,检测和数据自己之间的网络是否是通的
# 通了之后把事件的状态改为true,

import time,random
from threading import Thread,Event
def connect_db(e):
    count = 0
    while count < 3:
        # e.wait()  # 这个是一直等待,这种比较浪费资源,
        e.wait(0.1)  # 这是我只等待1秒,否则我就不等你了,# 但是我应该有一个重连的过程,比如三次,三次都连接不上,就断掉了
        if e.is_set() == True:
            print("连接数据库")
            break
        else:
            print("连接失败")
            count += 1
    else:
        raise TimeoutError("数据库连接超时")  # 主动报异常

def check_web(e):
    time.sleep(random.randint(0,2))
    e.set()

e = Event()
t1 = Thread(target=connect_db,args=(e,))
t2 = Thread(target=check_web,args=(e,))
t1.start()
t2.start()

###############  线程的条件和定时器    ##############

# 条件
from threading import Condition,Thread,Timer

# 可以把条件视为一个更加复杂的锁
# 同时也有两个方法,acquire,release
# 一个条件被创建,默认也是一个false状态,会影响wait处于等待状态
# notify(int),这是制造钥匙,参数是制造多少把钥匙
# 这个几乎就是只会出现在面试里面,平时基本不会用到,
#
# def func(con,i):
#     con.acquire()
#     con.wait()  # 在等待钥匙,
#     print("在第%s个循环里"%i)
#     con.release()
#
#
# con = Condition()
# for i in range(10):
#     Thread(target=func,args=(con,i)).start()
#
#
# while True:
#     num = int(input(">>>"))
#     con.acquire()
#     con.notify(num)  # 制造钥匙,
#     con.release()

# 定时器:
def func():
    print("时间同步")

Timer(2,func).start()  # 等待两秒钟开启一个线程,

###############    线程,队列  ##############

# 线程,队列

# 加锁会自己写代码,不方便,我们可以使用队列
# 队列内置了很多的锁,可以保证数据安全,

import queue

q = queue.Queue()

q.put()
q.put_nowait()  # 这个会报错
q.get()
q.get_nowait()  # 这个会报错,

# 队列的特点:先进先出,

queue.LifoQueue()  # 栈,先进后出,
q.put(1)
q.put(2)
q.put(3)
print(q.get())
print(q.get())
print(q.get())

queue.PriorityQueue()  # 优先级队列,
# 放数据的视乎,除了数据,还需要一个参数:优先级
q.put(20,1)
q.put(10,2)  # 数字越小,优先级越高,优先级一样,就是按照和ASk码排,
q.put(30,3)
print(q.get())
print(q.get())
print(q.get())

# 总结:
# 1,普通队列
# 2,栈
# 3,优先级队列,
# 这三种都不会出现多线程抢占资源,

###############   线程池    ##############

# 线程池

# 新的一个模块,
# 1 介绍
# concurrent.futures模块提供了高度封装的异步调用接口
# ThreadPoolExecutor:线程池,提供异步调用
# ProcessPoolExecutor: 进程池,提供异步调用

from concurrent.futures import ThreadPoolExecutor
import time
def func(n):
    time.sleep(2)
    print(n)
    return n*n

tpool = ThreadPoolExecutor(max_workers=5)
# 进程池,启动cpu核数+1.
# 而线程池的启动是cpu核数 * 5 不要超过这个,

t_list = []
for i in range(20):
    t = tpool.submit(func,i)  # 提交一个任务,传递一个参数,
    t_list.append(t)

tpool.shutdown()
# shutdown做了两个事情:
# 1,colse 关闭这个池子,不让有任务进来,
# 2,join是阻塞,直到这个池子的任务执行完,
# 所以是一个shutdown做了两个事情,
print("主进程")
for t in t_list:print(t.result())

###############    进程和线程的对比    ##############

进程和线程的对比:

面试的时候会问到,
进程是很多资源的总称,包括代码,包括显示器,包括鼠标,键盘等
线程是轻型的实现多任务的方式,
qq程序的多开,就是多个进程,
一个进程里面可以多个任务,这就是线程,
----------------------------
进程先有的,然后才有线程,
进程是一个资源分配的单位,具体谁拿着资源去做的呢,是线程,
一个进程里面一定有一个主线程,
--------------
所以多线程能实现多任务,是指的在一个进程里面有多个线程,实现多任务,
多进程的多任务,是有开辟了一个资源分配的单位,实现多任务,这个时候一个进程里面可能只有一个主线程,
真正执行代码的时候还是线程,
-------------
所以进程需要很大的资源才可以实现多任务,但是线程只需要很少的资源就可以实现多任务,
就像是同一个流水线,然后有很多的工人,
流水线就是进程,流水线上的工人就是一个线程,
所以开发的时候最长使用的是线程,这个比较轻,
------------
比如一个网易云音乐,既要下载音乐,又要播放音乐,
这就是一个进程,两个线程,
进程之间是独立的,
线程是依赖于进程的,没有进程就没有线程,
----------------------
所以这是非常的重要的,

###############    网络编程    ##############

原文地址:https://www.cnblogs.com/andy0816/p/12289718.html

时间: 2024-10-07 00:44:23

python语法基础-并发编程-线程-长期维护的相关文章

python语法基础-并发编程-进程-长期维护

###############    进程的启动方式1    ############## """ 并发编程: 进程 1,运行中的程序,就是进程,程序是没有生命的实体,运行起来了就有生命了, 操作系统可以管理进程,进程是操作系统基本的执行单元, 2,每一个进程都有它自己的地址空间,进程之间是不会混的,比如qq不能访问微信的地址空间, 操作系统替你隔离开了,这也是操作系统引入进程这个概念的原因, ####################################### 进

python语法基础-函数-装饰器-长期维护

###############    装饰器的初成和开放封闭原则    ############## # 装饰器 # 装饰器非常重要,面试Python的公司必问, # 原则:开放封闭原则 # # 需求:计算程序执行的时间, import time def func(): start = time.time() time.sleep(0.01) print(123) end = time.time() print(end-start) func() # 这种写法如果有100个函数,你都要加一遍这是

【python进阶】并发编程-线程与进程

并发编程-进程与线程 什么是进程(process)? 进程(process),是计算机中已运行程序的实体,是线程的容器:一个进程至少有一个线程 假如有两个程序A和B,程序A在执行到一半的过程中,需要读取大量的数据输入(I/O操作),而此时CPU只能静静地等待任务A读取完数据才能继续执行,这样就白白浪费了CPU资源.是不是在程序A读取数据的过程中,让程序B去执行,当程序A读取完数据之后,让 程序B暂停,然后让程序A继续执行?当然没问题,但这里有一个关键词:切换既然是切换,那么这就涉及到了状态的保存

python语法基础-网络编程-长期维护

###############    网络编程    ############## ###############    网络编程    ############## ###############    网络编程    ############## ###############    网络编程    ############## ###############    网络编程    ############## 原文地址:https://www.cnblogs.com/andy0816/p/

Python并发编程-线程

Python作为一种解释型语言,由于使用了全局解释锁(GIL)的原因,其代码不能同时在多核CPU上并发的运行.这也导致在Python中使用多线程编程并不能实现并发,我们得使用其他的方法在Python中实现并发编程. 一.全局解释锁(GIL) Python中不能通过使用多线程实现并发编程主要是因为全局解释锁的机制,所以首先解释一下全局解释锁的概念. 首先,我们知道C++和Java是编译型语言,而Python则是一种解释型语言.对于Python程序来说,它是直接被输入到解释器中直接运行的.解释器在程

Python中的并发编程

简介 我们将一个正在运行的程序称为进程.每个进程都有它自己的系统状态,包含内存状态.打开文件列表.追踪指令执行情况的程序指针以及一个保存局部变量的调用栈.通常情况下,一个进程依照一个单序列控制流顺序执行,这个控制流被称为该进程的主线程.在任何给定的时刻,一个程序只做一件事情. 一个程序可以通过Python库函数中的os或subprocess模块创建新进程(例如os.fork()或是subprocess.Popen()).然而,这些被称为子进程的进程却是独立运行的,它们有各自独立的系统状态以及主线

java并发编程线程安全

编写线程安全的代码实质就是管理对状态的访问,而且通常是共享的.可变的状态,对象的状态就是数据,存储在状态变量中,比如实例域,或者静态域,同时还包含了其它附属的域,例如hashmap的状态一部分存储到对象本身中,但同时也存储到很多mqp.entry中对象中,一个对象的状态还包含了任何会对他外部可见行为产生影响的数据. 所谓共享是指一个对象可以被多个线程访问, 所谓可变:是指变量的值在其生命周期内可以改变, 真正目的:在不可控制的并发访问中保护数据 线程安全必要条件: 1:对象是否被两个或以上的线程

17.并发编程--线程池

并发编程线程池 合理利用线程池能够带来三个好处. 第一:降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗. 第二:提高响应速度.当任务到达时,任务可以不需要的等到线程创建就能立即执行. 第三:提高线程的可管理性.线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控.但是要做到合理的利用线程池,必须对其原理了如指掌. 1. Executor 框架简介 在 Java 5 之后,并发编程引入了一堆新的启动.调度和管理 线

python基础-并发编程02

并发编程 子进程回收的两种方式 join()让主进程等待子进程结束,并回收子进程资源,主进程再结束并回收资源 from multiprocessing import Process import time def task(name): print(f'子进程{name}:starting--') time.sleep(1) print(f'子进程{name}:end--') if __name__ == '__main__': print('进入主进程--') pro_list = [] fo