进程
创建进程的方式
# -*- coding: utf-8 -*-
# @Time : 2023/8/14 18:22
# @Author : 4C69
# 方式1
from multiprocessing import Process
import time
def func(name):
    print(f'{name}任务开始')
    time.sleep(2)
    print(f'{name}任务执行完毕')
if __name__ == '__main__':
    # 1、得到进程操作对象
    p = Process(target=func, args=('li',))   # 注意args传入的是元组
    # 2、创建进程
    p.start()
    print('主进程')
    
    
# 运行结果
主进程
li任务开始
li任务执行完毕# -*- coding: utf-8 -*-
# @Time : 2023/8/14 18:22
# @Author : 4C69
# 方式2
from multiprocessing import Process
import time
class MyProcess(Process):
    def __init__(self, name):
        super().__init__()
        self.name = name
    def run(self) -> None:
        print(f'{self.name}开始')
        time.sleep(2)
        print(f'结束')
if __name__ == '__main__':
    p = MyProcess('li')
    p.start()
    print('主进程')
    
# 运行结果
主进程
li开始
结束Windows如果不加 if name == 'main':报错原因
在windows上创建进程,会使用类似模块导入的方式在子进程里面导入模块,也就是导入当前python文件,而导入模块会把模块里的代码都执行一遍,从而进入了死循环。
Linux,Unix中创建进程会把对应的代码以及当前进程的数据集(变量)全部都拷贝一份,然后在子进程中执行任务,所以Linux,Unix不会报错。
总结:创建进程就是在内存中申请一块内存空间,然后把需要运行的代码放进去,多个进程的内存空间它们彼此是隔离的,进程
与进程之间的数据,它们是没有办法直接交互的,如果需要交互,则可以借助第三方工具(模块)
join方法
join()等待子进程执行完毕后再执行主进程
# -*- coding: utf-8 -*-
# @Time : 2023/8/14 18:22
# @Author : 4C69
from multiprocessing import Process
import time
def func(name, n):
    print(f'{name}任务开始')
    time.sleep(n)
    print(f'{name}任务执行完毕')
if __name__ == '__main__':
    start = time.time()
    # p1 = Process(target=func, args=('写讲话稿', 1))
    # p2 = Process(target=func, args=('写讲话稿', 2))
    # p3 = Process(target=func, args=('写讲话稿', 3))
    # p4 = Process(target=func, args=('写讲话稿', 4))
    #
    # p1.start()
    # p2.start()
    # p3.start()
    # p4.start()
    #
    # p1.join()
    # p2.join()
    # p3.join()
    # p4.join()
    # 优化
    l  =[]
    for i in range(1, 5):
        p = Process(target=func, args=(f'写讲话稿{i}', i))
        p.start()
        l.append(p)
    for p in l:
        p.join()
    end = time.time()
    print(end-start)
    
    
# 运行结果
写讲话稿1任务开始
写讲话稿2任务开始
写讲话稿3任务开始
写讲话稿4任务开始
写讲话稿1任务执行完毕
写讲话稿2任务执行完毕
写讲话稿3任务执行完毕
写讲话稿4任务执行完毕
4.099806547164917进程之间的数据隔离
# -*- coding: utf-8 -*-
# @Time : 2023/8/8 18:08
# @Author : 4C69
from multiprocessing import Process
age = 18
def func():
    global age
    age = 16
if __name__ == '__main__':
    p = Process(target=func)
    p.start()
    p.join()
    print(age)
    
# 运行结果
18不同进程之间的数据是隔离的,子进程起来的时候会复制主进程里面的变量
进程号
pid号(进程号)
PID全称为:Process Identifier
系统每打开一个程序,就会分配一个进程编码
进程ID是暂时,也是唯一的
比如:Chrome.exe占用了17652的PID,该进程在没有关闭之前,则会一直使用17652的PID,其 它的进程在此之前都无法使用该PID,这就是它的唯一性。
当Chrome.exe关闭后再开启,系统则会重新匹配PID,可能不是原来的那个17652的PID,这就是它的暂时性。
# -*- coding: utf-8 -*-
# @Time : 2023/8/8 18:08
# @Author : 4C69
from multiprocessing import Process,current_process
import time
import os
def task(name='子进程'):
    print(f'任务{current_process().pid}执行中')
    print(f'{name}{os.getpid()}执行中')
    print(f'{name}的父进程{os.getppid()}执行中')
if __name__ == '__main__':
    p = Process(target=task)
    p.start()
    # p.terminate()  杀死当前进程(win: taskkill pid, mac/linux: kill -9 pid)
    time.sleep(1)
    # print(p.is_alive())    判断当前进程是否存活,返回bool值
    print('主进程')
    task('主进程')
    
    
#运行结果
任务23656执行中
子进程23656执行中
子进程的父进程34104执行中
主进程
任务34104执行中
主进程34104执行中
主进程的父进程21648执行中   #主进程的父进程,因为当前的python文件也要运行僵尸进程和孤儿进程
- 僵尸进程 
"""
⼦进程死后,还会有⼀些资源占⽤(进程号,进程运⾏状态,运⾏
时间等),等待⽗进程通过系统调⽤回收(收⼫)
除了init进程之外,所有的进程,最后都会步⼊僵⼫进程
危害:
⼦进程退出之后,⽗进程没有及时处理,僵⼫进程就会⼀直占
⽤计算机资源
如果产⽣了⼤量的僵⼫进程,资源过度占⽤,系统没有可⽤的
进程号,导致系统不能产⽣新的进程
"""- 孤儿进程 
"""
⼦进程处于存活状态,但是⽗进程意外死亡
操作系统会开设⼀个“孤⼉院”(init进程),⽤来管理孤⼉进
程,回收孤⼉进程的相关资源
"""守护进程
一个进程守护另一个进程
守护进程一般在系统启动时开始运行,除非强行终止,否则直到系统关机才随之一起停止运行。
# -*- coding: utf-8 -*-
# @Time : 2023/8/8 18:08
# @Author : 4C69
from multiprocessing import Process
import time
def task(name):
    print(f'{name}还活着')
    time.sleep(5)
    print(f'{name}正常死亡')
if __name__ == '__main__':
    p = Process(target=task, args=('苏妲己',))
    p.start()
    print(f'纣王驾崩了')
    
    
# 运行结果
纣王驾崩了
苏妲己还活着
苏妲己正常死亡# -*- coding: utf-8 -*-
# @Time : 2023/8/8 18:08
# @Author : 4C69
from multiprocessing import Process
import time
def task(name):
    print(f'{name}还活着')
    time.sleep(5)
    print(f'{name}正常死亡')
if __name__ == '__main__':
    p = Process(target=task, args=('苏妲己',))
    p.daemon = True
    p.start()
    time.sleep(2)
    print(f'纣王驾崩了')
    
    
# 运行结果
苏妲己还活着
纣王驾崩了互斥锁
当多个进程操作同一份数据的时候,会出现数据错乱的问题,解决方法就是加锁处理。
把并发变成串行,牺牲了效率,但保证了数据的安全
注意:加锁只应该在争抢数据的环节加
# tickets.json
{"tickets_num": 2}# -*- coding: utf-8 -*-
# @Time : 2023/8/20 21:04
# @Author : 4C69
from multiprocessing import Process,Lock
import random
import time
import json
# 查票
def search_ticket(name):
    with open(r'data/tickets.json', mode='rt', encoding='utf-8')as f:
        dic = json.load(f)
        print(f'用户{name}查询余票:{dic.get("tickets_num")}')
# 买票
def buy_ticket(name):
    with open(r'data/tickets.json', mode='rt', encoding='utf-8') as f:
        dic = json.load(f)
    time.sleep(random.randint(1,5))
    if dic['tickets_num'] > 0:
        dic['tickets_num'] -= 1
        with open(r'data/tickets.json', mode='wt', encoding='utf-8')as f:
            json.dump(dic, f)
        print(f'用户{name}购买成功')
    else:
        print(f'余额不足,用户{name}买票失败')
def task(name):
    search_ticket(name)
    buy_ticket(name)
if __name__ == '__main__':
    mutex = Lock()
    for i in range(1,11):
        p = Process(target=task, args=(f'{i}',  ))
        p.start()
        
# 结果
用户1查询余票:2
用户2查询余票:2
用户3查询余票:2
用户4查询余票:2
用户5查询余票:2
用户6查询余票:2
用户7查询余票:2
用户8查询余票:2
用户9查询余票:2
用户10查询余票:2
用户2购买成功
用户4购买成功
用户7购买成功
用户10购买成功
用户1购买成功
用户3购买成功
用户6购买成功
用户9购买成功
用户8购买成功
用户5购买成功# -*- coding: utf-8 -*-
# @Time : 2023/8/20 21:04
# @Author : 4C69
# 加锁处理后
from multiprocessing import Process,Lock
import random
import time
import json
# 查票
def search_ticket(name):
    with open(r'data/tickets.json', mode='rt', encoding='utf-8')as f:
        dic = json.load(f)
        print(f'用户{name}查询余票:{dic.get("tickets_num")}')
# 买票
def buy_ticket(name):
    with open(r'data/tickets.json', mode='rt', encoding='utf-8') as f:
        dic = json.load(f)
    time.sleep(random.randint(1,5))
    if dic['tickets_num'] > 0:
        dic['tickets_num'] -= 1
        with open(r'data/tickets.json', mode='wt', encoding='utf-8')as f:
            json.dump(dic, f)
        print(f'用户{name}购买成功')
    else:
        print(f'余额不足,用户{name}买票失败')
def task(name, mutex):
    search_ticket(name)
    # 加锁
    mutex.acquire()
    buy_ticket(name)
    # 释放锁
    mutex.release()
if __name__ == '__main__':
    mutex = Lock()
    for i in range(1,11):
        p = Process(target=task, args=(f'{i}', mutex))
        p.start()
        
        
# 结果
用户2查询余票:2
用户1查询余票:2
用户3查询余票:2
用户4查询余票:2
用户5查询余票:2
用户6查询余票:2
用户7查询余票:2
用户8查询余票:2
用户9查询余票:2
用户10查询余票:2
用户2购买成功
用户1购买成功
余额不足,用户3买票失败
余额不足,用户4买票失败
余额不足,用户5买票失败
余额不足,用户6买票失败
余额不足,用户7买票失败
余额不足,用户8买票失败
余额不足,用户9买票失败
余额不足,用户10买票失败消息队列
队列:先进先出
管道+锁(数据取了一份后就没有了)
堆栈:后进先出
# -*- coding: utf-8 -*-
# @Time : 2023/8/20 21:04
# @Author : 4C69
from multiprocessing import Queue
q = Queue(6)
q.put('a')
q.put('b')
q.put('c')
q.put('d')
q.put('e')
q.put('f')
q.put('g')
v1 = q.get()
print(v1)
# 运行结果
程序会阻塞# -*- coding: utf-8 -*-
# @Time : 2023/8/20 21:04
# @Author : 4C69
from multiprocessing import Queue
q = Queue(6)
q.put('a')
q.put('b')
q.put('c')
q.put('d')
q.put('e')
q.put('f', timeout=3)   #timeout设置超时时间,存储数据时,如果队列已经满了,3秒之内如果还是没满,也会直接报错x
q.put_nowait('g')
v1 = q.get()
print(v1)
# 运行结果
Traceback (most recent call last):
  File "C:\Users\4C69\PycharmProjects\python_basics\并发编程\消息队列.py", line 15, in <module>
    q.put_nowait('g')
  File "C:\Users\4C69\AppData\Local\Programs\Python\Python37\lib\multiprocessing\queues.py", line 129, in put_nowait
    return self.put(obj, False)
  File "C:\Users\4C69\AppData\Local\Programs\Python\Python37\lib\multiprocessing\queues.py", line 83, in put
    raise Full
queue.Full# -*- coding: utf-8 -*-
# @Time : 2023/8/20 21:04
# @Author : 4C69
from multiprocessing import Queue
q = Queue(6)
q.put('a')
q.put('b')
q.put('c')
q.put('d')
q.put('e')
q.put('f')
v1 = q.get()
v2 = q.get()
v3 = q.get()
v4 = q.get()
v5 = q.get()
v6 = q.get()
v7 = q.get()
print(v1, v2, v3, v4, v5, v6, v7)
# 运行结果
程序会阻塞(等待v7)# -*- coding: utf-8 -*-
# @Time : 2023/8/20 21:04
# @Author : 4C69
from multiprocessing import Queue
q = Queue(6)
q.put('a')
q.put('b')
q.put('c')
q.put('d')
q.put('e')
q.put('f')
v1 = q.get()
v2 = q.get()
v3 = q.get()
v4 = q.get()
v5 = q.get()
v6 = q.get()
v7 = q.get_nowait()
print(v1, v2, v3, v4, v5, v6, v7)
# 运行结果
C:\Users\4C69\AppData\Local\Programs\Python\Python37\python.exe C:\Users\4C69\PycharmProjects\python_basics\并发编程\消息队列.py 
Traceback (most recent call last):
  File "C:\Users\4C69\PycharmProjects\python_basics\并发编程\消息队列.py", line 23, in <module>
    v7 = q.get_nowait()
  File "C:\Users\4C69\AppData\Local\Programs\Python\Python37\lib\multiprocessing\queues.py", line 126, in get_nowait
    return self.get(False)
  File "C:\Users\4C69\AppData\Local\Programs\Python\Python37\lib\multiprocessing\queues.py", line 107, in get
    raise Empty
_queue.Empty# -*- coding: utf-8 -*-
# @Time : 2023/8/20 21:04
# @Author : 4C69
from multiprocessing import Queue
q = Queue(6)
q.put('a')
q.put('b')
q.put('c')
q.put('d')
q.put('e')
q.put('f')
print(q.full())    # 判断队列是否已满
v1 = q.get()
v2 = q.get()
v3 = q.get()
v4 = q.get()
v5 = q.get()
v6 = q.get()
print(q.empty())    # 判断队列是否为空
print(v1, v2, v3, v4, v5, v6)
# 运行结果
True
True
a b c d e f"""
q.put()
q.get()
# 在多进程下可能不准
q.put_nowait()
q.get_nowait()
q.full()
q.empty()
"""进程间的通信(IPC机制)
- 主进程和子进程之间的通信 
# -*- coding: utf-8 -*-
# @Time : 2023/8/25 19:34
# @Author : 4C69
from multiprocessing import Process, Queue
def task1(q):
    q.put('宫保鸡丁')
if __name__ == '__main__':
    q = Queue()
    p = Process(target=task1, args=(q,))
    p.start()
    print(q.get())
    
    
# 运行结果
宫保鸡丁- 子进程和子进程之间的通信 
# -*- coding: utf-8 -*-
# @Time : 2023/8/25 19:34
# @Author : 4C69
from multiprocessing import Process, Queue
def task1(q):
    q.put('宫保鸡丁')
def task2(q):
    print(q.get())
if __name__ == '__main__':
    q = Queue()
    p1 = Process(target=task1, args=(q,))
    p2 = Process(target=task2, args=(q,))
    p1.start()
    p2.start()
    
    
    
# 运行结果
宫保鸡丁生产消费者模型
生产者(厨师):生产或者制造数据的
消费者(顾客):消费或者处理数据的
媒介 (桌子)
# -*- coding: utf-8 -*-
# @Time : 2023/8/25 19:52
# @Author : 4C69
from multiprocessing import Process,JoinableQueue
import time
import random
'''
JoinableQueue
在Queue的基础上多了一个计数器机制,每put一个数据,计数器就加一
每调用一次task_done,计数器就减一
当计数器为0的时候,就会走q.join后面的代码
'''
def producer(name, food, q):
    for i in range(8):
        time.sleep(random.randint(1,3))
        print(f'{name}生产了{food}{i}')
        q.put(f'{food}{i}')
def consumer(name, q):
    while True:
        food = q.get()
        time.sleep(random.randint(1,3))
        print(f'{name}吃了{food}')
        q.task_done()    # 告诉队列,已经拿走了一个数据,并且已经处理完了
if __name__ == '__main__':
    q = JoinableQueue()
    p1 = Process(target=producer, args=('中华小当家', '黄金炒饭', q))
    p2 = Process(target=producer, args=('神厨小福贵', '佛跳墙', q))
    c1 = Process(target=consumer, args=('八戒', q))
    c2 = Process(target=consumer, args=('悟空', q))
    p1.start()
    p2.start()
    c1.daemon = True
    c2.daemon = True
    c1.start()
    c2.start()
    p1.join()
    p2.join()
    q.join()	# 等待队列中所有的数据被取完,计数器变成0
    # 主进程死了,消费者也要跟着陪葬,守护进程
    
    
# 运行结果
神厨小福贵生产了佛跳墙0
中华小当家生产了黄金炒饭0
神厨小福贵生产了佛跳墙1八戒吃了佛跳墙0
中华小当家生产了黄金炒饭1
悟空吃了黄金炒饭0
中华小当家生产了黄金炒饭2
中华小当家生产了黄金炒饭3八戒吃了佛跳墙1神厨小福贵生产了佛跳墙2
悟空吃了黄金炒饭1
中华小当家生产了黄金炒饭4
八戒吃了黄金炒饭2
悟空吃了黄金炒饭3
神厨小福贵生产了佛跳墙3
中华小当家生产了黄金炒饭5
八戒吃了佛跳墙2
神厨小福贵生产了佛跳墙4悟空吃了黄金炒饭4
中华小当家生产了黄金炒饭6
八戒吃了佛跳墙3
悟空吃了黄金炒饭5
神厨小福贵生产了佛跳墙5
悟空吃了黄金炒饭6
中华小当家生产了黄金炒饭7
悟空吃了佛跳墙5
八戒吃了佛跳墙4
神厨小福贵生产了佛跳墙6
悟空吃了黄金炒饭7
八戒吃了佛跳墙6
神厨小福贵生产了佛跳墙7
悟空吃了佛跳墙7线程
进程:资源单位
线程:执行单位
把操作系统比喻成一个工厂,进程相当于工厂里的车间,线程相当于车间里的流水线。
创建进程
- 申请内存空间 消耗资源 
- 拷贝代码 消耗资源 
创建线程:在一个进程内可以创建多个线程,同一个进程内,多个线程之间的资源是共享的
- 不需要再次申请内存空间 
- 不需要拷贝代码 
创建线程的方式
# -*- coding: utf-8 -*-
# @Time : 2023/8/29 12:54
# @Author : 4C69
# 方式二
from threading import Thread
import time
def task(name):
    print(f'{name} 任务开始')
    time.sleep(1)
    print(f'{name} 任务结束')
if __name__ == '__main__':  # 创建线程可以不用放在__main__下面
    t = Thread(target=task, args=('悟空',))
    t.start()
    print('主线程')
    
    
# 运行结果
悟空 任务开始
主线程
悟空 任务结束# -*- coding: utf-8 -*-
# @Time : 2023/8/29 12:54
# @Author : 4C69
# 方式二
from threading import Thread
import time
class MyThread(Thread):
    def __init__(self, name):
        super().__init__()
        self.name = name
    def run(self) -> None:
        print(f'{self.name} 任务开始')
        time.sleep(3)
        print(f'{self.name} 任务结束')
if __name__ == '__main__':
    m = MyThread('悟空')
    m.start()
    print('主线程')
    
# 运行结果
悟空 任务开始
主线程
悟空 任务结束tcp并发
# -*- coding: utf-8 -*-
# @Time : 2023/8/29 13:30
# @Author : 4C69
# tcp服务端
from multiprocessing import Process
import socket
def task(conn):
    while True:
        try:
            data = conn.recv(1024)
        except:
            break
        if not data:
            break
        print(data.decode('utf-8'))
        conn.send(data.upper())
    conn.close()
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sk.bind(('127.0.0.1', 8002))
sk.listen(5)
if __name__ == '__main__':
    while True:
        conn, addr = sk.accept()
        p = Process(target=task, args=(conn, ))
        p.start()
        
  
# Windows运行后会发现报错(OSError: [WinError 10048] 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。)而mac和Linux不会,因为window创建进程时会把对应的代码以及当前进程的数据集(变量)全部都拷贝一份,创建进程的时候又创建了一个套接字,bind了相同的端口
# 解决方法:把bind放进__main__
from multiprocessing import Process
import socket
def task(conn):
    while True:
        try:
            data = conn.recv(1024)
        except:
            break
        if not data:
            break
        print(data.decode('utf-8'))
        conn.send(data.upper())
    conn.close()
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
if __name__ == '__main__':
    sk.bind(('127.0.0.1', 8080))
    sk.listen(5)
    while True:
        conn, addr = sk.accept()
        p = Process(target=task, args=(conn, ))
        p.start()# -*- coding: utf-8 -*-
# @Time : 2023/8/29 13:06
# @Author : 4C69
# tcp客户端
import socket
import time
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sk.connect(('127.0.0.1', 8080))
while True:
    sk.send(b'hello')
    data = sk.recv(1024)
    print(data.decode('utf-8'))
    time.sleep(2)join方法
# -*- coding: utf-8 -*-
# @Time : 2023/8/30 12:33
# @Author : 4C69
from threading import Thread
import time
def task(name):
    print(f'{name} 任务开始')
    time.sleep(3)
    print(f'{name} 任务结束')
if __name__ == '__main__':
    t = Thread(target=task, args=('悟空', ))
    t.start()
    t.join()    # 主线程等待子线程运行结束之后才会往下执行
    print('主线程')
    
    
# 运行结果    
悟空 任务开始
悟空 任务结束
主线程线程之间的数据共享
# -*- coding: utf-8 -*-
# @Time : 2023/8/30 14:08
# @Author : 4C69
from threading import Thread
import os
def task():
    print('子线程', os.getpid())
if __name__ == '__main__':
    t = Thread(target=task)
    t.start()
    print('主线程', os.getpid())
    
    
# 运行结果
子线程 9628
主线程 9628- 说明主线程和子线程是同一个进程内的 
# -*- coding: utf-8 -*-
# @Time : 2023/8/30 14:08
# @Author : 4C69
from threading import Thread
import os
age = 18
def task():
    global age
    age = 16
if __name__ == '__main__':
    t = Thread(target=task)
    t.start()
    print(age)
    
    
# 运行结果
16- 说明同一个进程内,不同的线程之间数据是共享的 
# -*- coding: utf-8 -*-
# @Time : 2023/8/30 14:08
# @Author : 4C69
from threading import Thread, current_thread, active_count
import os
def task():
    print(current_thread().name)    # 获取当前进程的名字(名字是自动生成的)
if __name__ == '__main__':
    t = Thread(target=task)
    t1 = Thread(target=task)
    t.start()
    t1.start()
    print(current_thread().name)
    print('活跃的线程数量', active_count())    # 统计活动的线程数量
    
    
# 运行结果
Thread-1
Thread-2
MainThread
活跃的线程数量 2
会发现一共3个进程(2个子线程,1个主线程)但它打印了2个,原因是开设线程不怎么消耗资源,所以线程起来的速度很快,同时函数里的代码非常简单,所以打印的时候第一个起来的线程已经结束了
解决方法:在函数里加个延时
from threading import Thread, current_thread, active_count
import os
import time
def task():
    print(current_thread().name)    # 获取当前进程的名字(名字是自动生成的)
    time.sleep(1)
if __name__ == '__main__':
    t = Thread(target=task)
    t1 = Thread(target=task)
    t.start()
    t1.start()
    print(current_thread().name)
    print('活跃的线程数量', active_count())    # 统计活动的线程数量
    
    
# 运行结果
Thread-1
Thread-2
MainThread
活跃的线程数量 3守护线程
一个线程守护另一个线程,主线程死后,守护线程也会跟着死。
# -*- coding: utf-8 -*-
# @Time : 2023/8/30 14:08
# @Author : 4C69
from threading import Thread
import time
"""
主线程运行完毕之后,它不会立刻结束,要等待所有子线程运行完毕之后才会结束;
因为主线程结束,就意味着主线程所在的进程结束了
"""
def task(name):
    print(f'{name}还活着')
    time.sleep(3)
    print(f'{name} 正常死亡')
if __name__ == '__main__':
    t = Thread(target=task, args=('妲己', ))
    t.start()
    print('纣王驾崩')守护线程的实现,主线程结束,子线程也跟着结束
# -*- coding: utf-8 -*-
# @Time : 2023/8/30 14:08
# @Author : 4C69
from threading import Thread
import time
def task(name):
    print(f'{name}还活着')
    time.sleep(3)
    print(f'{name} 正常死亡')
if __name__ == '__main__':
    t = Thread(target=task, args=('妲己', ))
    t.daemon = True
    t.start()
    print('纣王驾崩')
    
  
# 运行结果
妲己还活着
纣王驾崩例子:
# -*- coding: utf-8 -*-
# @Time : 2023/8/30 14:08
# @Author : 4C69
from threading import Thread
import time
def f1():
    print('任务1开始')
    time.sleep(1)
    print('任务1结束')
def f2():
    print('任务2开始')
    time.sleep(2)
    print('任务2结束')
if __name__ == '__main__':
    t1 = Thread(target=f1)
    t2 = Thread(target=f2)
    t1.daemon = True
    t1.start()
    t2.start()
    print('主线程')
    
    
# 运行结果
任务1开始
任务2开始
主线程
任务1结束
任务2结束线程互斥锁
# -*- coding: utf-8 -*-
# @Time : 2023/8/30 14:08
# @Author : 4C69
from threading import Thread
import time
num = 180
def task():
    global num
    temp = num
    time.sleep(0.05)
    num = temp - 1
if __name__ == '__main__':
    l = []
    for i in range(180):
        t = Thread(target=task)
        t.start()
        l.append(t)
    for t in l:
        t.join()
    print(num)
    
    
# 运行结果
179
原因是:所有的线程都共同拿到temp然后又共同睡了0.05s,然后又共同减了1解决方法:多个人操作一份数据,加锁处理把并发变为串行
# -*- coding: utf-8 -*-
# @Time : 2023/8/30 14:08
# @Author : 4C69
# 写法一:
from threading import Thread, Lock
import time
num = 180
def task(mutex):
    global num
    mutex.acquire()    #获取数据抢锁
    temp = num
    time.sleep(0.05)
    num = temp - 1
    mutex.release()    #数据处理完之后释放锁
if __name__ == '__main__':
    mutex = Lock()
    l = []
    for i in range(180):
        t = Thread(target=task, args=(mutex, ))
        t.start()
        l.append(t)
    for t in l:
        t.join()
    print(num)
    
    
# 写法二
num = 180
mutex = Lock()
def task():
    global num
    mutex.acquire()    #获取数据抢锁
    temp = num
    time.sleep(0.05)
    num = temp - 1
    mutex.release()    #数据处理完之后释放锁
if __name__ == '__main__':
    l = []
    for i in range(180):
        t = Thread(target=task)
        t.start()
        l.append(t)
    for t in l:
        t.join()
    print(num)
    
# 之所以可以把mutex放进上面是因为,线程之间数据共享
GIL全局解释器锁
https://wiki.python.org/moin/GlobalInterpreterLock?action=show&redirect=GIL
In CPython, the global interpreter lock, or GIL, is a mutex that protects access to Python objects, preventing multiple threads from executing Python bytecodes at once. The GIL prevents race conditions and ensures thread safety. A nice explanation of how the Python GIL helps in these areas can be found here. In short, this mutex is necessary mainly because CPython's memory management is not thread-safe.
在CPython中,全局解释器锁或GIL是一个互斥锁,用于保护对Python对象的访问,防止多个线程同时执行Python字节码。GIL防止竞争条件并确保线程安全。关于Python GIL如何在这些领域提供帮助的一个很好的解释可以在这里找到(https://python.land/python-concurrency/the-python-gil)。简而言之,这个互斥锁是必要的,主要是因为CPython的内存管理不是线程安全的。
内存管理(垃圾回收机制)
- 引用计数 
- 标记清除 
- 分代回收 - GC(Garbage cleaning)巡逻 
Python解释器版本
- Cpython 
- Jpython 
- Pypypython 
注意:
- GIL不是python的特点,而是Cpython解释器独有的特点 
- GIL会导致同一个进程下的多个线程不能同时执行,无法利用多核 
 
          
        