目录

  • 基础知识
  • 多进程
    • 多进程间执行先后
    • 进程通信(pipe queue)
  • 多线程
    • 线程安全
    • GIL与Lock锁
    • 线程进程总结
    • 线程池
  • 高并发拷贝(多进程,多线程)

说明
相应的学习视频见链接,本文只对重点进行总结。

B站视频-黑马程序,以及相应的黑马视频
课程同步的CSDN,写的细致

基础知识

多进程

重点(只要看下面代码的main函数即可)

  1. 创建
  2. 如何开守护进程
  3. 多进程,开销大,用for循环调用多个进程时,后台cpu一下就上去了
import time
import multiprocessing
import os
def dance(who,num):
    print("dance父进程:{}".format(os.getppid()))
    for i in range(1,num+1):
        print("进行编号:{}————{}跳舞。。。{}".format(os.getpid(),who,i))
        time.sleep(0.5)


def sing(num):
    print("sing父进程:{}".format(os.getppid()))
    for i in range(1,num+1):
        print("进行编号:{}----唱歌。。。{}".format(os.getpid(),i))
        time.sleep(0.5)

def work():
    for i in range(10):
        print("工作中。。。")
        time.sleep(0.2)


if __name__ == '__main__':
    # print("main主进程{}".format(os.getpid()))
    start= time.time()

    #1 进程的创建与启动
    # # 1.1创建进程对象,注意dance不能加括号
    # # dance_process = multiprocessing.Process(target=dance)#1.无参数
    # dance_process=multiprocessing.Process(target=dance,args=("lin",3))#2.以args=元祖方式
    # sing_process = multiprocessing.Process(target=sing,kwargs={"num":3})#3.以kwargs={}字典方式
    # # 1.2启动进程
    # dance_process.start()
    # sing_process.start()

    #2.默认-主进程和子进程是分开的,主进程只要1s就可以完成,子进程要2s,主进程会等所有子进程执行完,再退出
    # 2.1子守护主进程,当主一但完成,子就断开(如qq一关闭,所有聊天窗口就没了).daemon=True
    work_process = multiprocessing.Process(target=work,daemon=True)
    work_process.start()

    time.sleep(1)
    print("主进程完成了!")#主进程和子进程是分开的,主进程只要1s就可以完成,子进程要2s,主进程会等所有子进程执行完,再退出
    print("main主进程花费时长:",time.time()-start)
    #

多进程间执行先后

  1. 默认情况下,主进程会等所有子进程执行完,再退出。
  2. 子进程设置成守护进程后,当主进程一旦完成,子进程立马结束
    dance_process=multiprocessing.Process(target=dance,args=(“lin”,3),daemon=True)
  3. 使用join()方法加塞,只有当设置的子进程结束后,才会开始主进程
    sing_process.start()
    dance_process.join()
    print(“main主进程{}”.format(os.getpid()))

进程通信(pipe queue)

主要Queue和Pipe这两种方式,Queue用于多个进程间实现通信,Pipe是两个进程的通信

多线程


重点

  1. 创建
  2. 守护线程
  3. 线程安全问题(多人抢票,会抢到同一张)
import time
import os
import threading
def dance(num):

    for i in range(num):
        print("进程编号:{},线程编号:{}————跳舞。。。".format(os.getpid(),threading.current_thread()))
        time.sleep(1)


def sing(count):
    for i in range(count):
        print("进程编号:{},线程编号:{}----唱歌。。。".format(os.getpid(),threading.current_thread()))
        time.sleep(1)

def task():
    time.sleep(1)
    thread=threading.current_thread()
    print(thread)

if __name__ == '__main__':
    # start=time.time()
    # # sing_thread =threading.Thread(target=dance,args=(3,),daemon=True)#设置成守护主线程
    # sing_thread = threading.Thread(target=dance, args=(3,))
    # dance_thread = threading.Thread(target=sing,kwargs={"count":3})
    #
    # sing_thread.start()
    # dance_thread.start()
    #
    # time.sleep(1)
    # print("进程编号:{}主线程结束...用时{}".format(os.getpid(),(time.time()-start)))

    for i in range(10):#多线程之间执行是无序的,由cpu调度
        sub_thread = threading.Thread(target=task)
        sub_thread.start()

线程安全

B站链接
由于线程直接是无序进行的,且他们共享同一个进程的全部资源,所以会产生线程安全问题(比如多人在线抢票,买到同一张)



#下面代码在没有lock锁时,会卖出0票,加上lock就正常

import threading
import time
lock =threading.Lock()
class Sum_tickets:
    def __init__(self,tickets):
        self.tickets=tickets

def window(sum_tickets):

    while True:
        with lock:
            if sum_tickets.tickets>0:
                time.sleep(0.2)
                print(threading.current_thread().name,"取票{}".format(sum_tickets.tickets))
                sum_tickets.tickets-=1
            else:
                break

if __name__ == '__main__':
    sum_tickets=Sum_tickets(10)
    sub_thread1 = threading.Thread(name="窗口1",target=window,args=(sum_tickets,))
    sub_thread2 = threading.Thread(name="窗口2",target=window,args=(sum_tickets,))
    sub_thread1.start()
    sub_thread2.start()

GIL与Lock锁

参考1
参考2
由于多线程是资源共享的,所以会带来线程安全问题,所以要加锁进行保护,保证同一时刻只能有一个线程来使用。
python提供两种锁,一个gil全局解释器锁,在多线程的过程中,比如垃圾回收线程,和普通线程会发生冲突,所以加了gil,保证同一时刻只有一个线程可以执行,正是由于这种机制,python下的多线程只能并发,并不是真正意义上的多线程。
第二个是lock锁,保证共享数据在某一时间内只能被一个线程修改,当该线程释放锁后,其他线程才能修改共享数据,让多线程变成了一种串行模式。

线程进程总结

  1. 线程是依附在进程里面的,没有进程就没有线程,一个进程可以创建多个线程
  2. 进程之间的资源相互独立,而同一进程下的线程共享资源。一个进程崩了,不会影响其他,而一个线程崩了产生阻塞,整个进程就没了。
  3. 进程是操作系统 资源分配的基本单位,比创建线程的资源开销要大,适用于cpu密集型工作(指大量的运算工作,比如上亿次浮点运算,占用大量cpu)
  4. 线程是 处理器调度的基本单位(程序执行的基本单位),资源开销小,适用于I/O密集型工作(web来回爬取网站信息,硬盘传输文件)

线程池


[参考视频]—线程池的使用(https://www.bilibili/video/BV1zy4y1M7xt?p=8&spm_id_from=pageDriver)

from concurrent.futures import  ThreadPoolExecutor,as_completed,wait,FIRST_COMPLETED
#1.主线程可以获取某一个线程的状态,以及线程函数的返回值
#2.futures可以让多进程和多线程编码接口一致

import time

def get_html(t):
    time.sleep(t)
    print("get page {} success".format(t))
    return t

# #1.样例的单个输入,submit提交执行函数,立即执行不会阻塞
# executor = ThreadPoolExecutor(max_workers=1)
# task1=executor.submit(get_html,(3))
# task2=executor.submit(get_html,(2))
# task2.cancel()
# print(task1.done())#查看线程是否完成,由于立即执行不会阻塞,所以状态False
# print(task1.result())#获取task执行结果
# print(task2.cancel())#False,当线程已经开始时,是无法cancel的,返回为false,当处于等待时进入pool时,是可取消的


#2.使用list,进行样例列表的输入
executor = ThreadPoolExecutor(max_workers=2)
urls=[3,2,4]
all_tasks=[executor.submit(get_html,(url)) for url in urls]
wait(all_tasks,return_when=FIRST_COMPLETED)#使用wait,阻塞主线程,这里设置当第一个线程执行完,才能进行主线程
print('main')
##法1:先完成的线程,先返回
for future in as_completed(all_tasks):#as_completed 将已完成的task找出来,已完成的顺序可以与输入顺序不同
    data=future.result()
    print("get {} page".format(data))

# ##2:按照输入的任务顺序,进行返回
# for data in executor.map(get_html,urls):#注意map返回的结果,就是上面future.result()
#     print("get {} page".format(data))

高并发拷贝(多进程,多线程)


import os
import multiprocessing
import threading
import time
def copy_file(file_name,source_dir,dest_dir):
    source_path = source_dir+"/"+file_name
    dest_path =dest_dir+"/"+file_name
    print("当前进程为:{}".format(os.getpid()))
    with open(source_path,"rb") as source_file:
        with open(dest_path,"wb") as dest_file:
            while True:
                data=source_file.read(1024)

                if data:
                    dest_file.write(data)
                else:
                    break

    pass

if __name__ == '__main__':
    source_dir=r'C:\Users\Administrator\Desktop\注意力'
    dest_dir=r'C:\Users\Administrator\Desktop\test'

    start = time.time()
    try:
        os.mkdir(dest_dir)
    except:
        print("目标文件已存在")
    file_list =os.listdir(source_dir)
    count=0

    #1多进程
    for file_name in file_list:
        count+=1
        print(count)
        sub_processor=multiprocessing.Process(target=copy_file,
                                args=(file_name,source_dir,dest_dir))
        sub_processor.start()
        # time.sleep(20)
    print(time.time()-start)
#这里有主进程和子进程,通过打印可以看出,主进程在创建1,2,3,4,,,21过程中,子进程已有的开始执行,也就是说,每个进程是互不影响的
# 9
# 10
# 11
# 12
# 13
# 当前进程为:2936(当主进程创建第13个时,此时,第一个子进程开始工作)
# 14
# 当前进程为:10120
# 当前进程为:10440
# 15
# 当前进程为:9508

    # 2多线程
    # for file_name in file_list:
    #     count += 1
    #     print(count)
    #     sub_thread = threading.Thread(target=copy_file,
    #                                             args=(file_name, source_dir, dest_dir))
    #     sub_thread.start()
    #     # time.sleep(20)
    # print(time.time() - start)


更多推荐

python多进程、多线程、(并发\并行\串行)