Python - 多线程与多进程

x33g5p2x  于2021-09-19 转载在 Python  
字(4.5k)|赞(0)|评价(0)|浏览(243)

多线程

程序默认都是单线程(这个默认线程又叫主线程,其他的线程都叫子线程)

Thread类的对象就是线程对象,程序需要多少个子线程就创建多少个Thread的对象

import time
from datetime import  datetime
from threading import Thread,current_thread

模拟电影下载线程:

def download(movie_name:str):
    print(f'《{movie_name}》开始下载:{datetime.now()}')
    print(current_thread())
    time.sleep(2)
    print(f'《{movie_name}》下载结束:{datetime.now()}')

单线程下载三部电影

download('天若有情')
download('英雄本色')
download('使徒行者')

多线程下载三部电影

创建子线程:

Thread(target = 函数,args = (元组))

函数 — 需要在子线程中调用的函数

元组 — 调用target对应的函数的时候传递的实参列表

1)、创建线程对象

t1 = Thread(target=download,args=('天若有情',))
t2 = Thread(target=download,args=('英雄本色',))
t3 = Thread(target=download,args=('使徒行者',))

2)、启动线程

线程对象.start()

t1.start()
t2.start()
t3.start()

线程类的子类对象

from threading import Thread,current_thread
import time
from datetime import datetime
class DownLoadThread(Thread):
    def __init__(self,movie_name):
        super().__init__()   #调用父类__init__
        self.movie_name = movie_name
    def run(self) -> None:    #返回值类型说明,none说明没有返回值
        print(f'《{self.movie_name}》开始下载:{datetime.now()}')
        print(current_thread())
        time.sleep(2)
        print(f'《{self.movie_name}》下载结束:{datetime.now()}')

download = DownLoadThread('英雄本色')
download1 = DownLoadThread('天若有情')
download2 = DownLoadThread('使徒行者')

# 通过start()调用run(),run()方法会在相应的子线程中调用
download.start()
download1.start()
download2.start()

join操作

import time
from datetime import  datetime
from threading import Thread,current_thread
from random import randint

join的用法:

线程对象.join() — 等待当前线程的任务结束后才执行后面的代码

def download(movie_name:str):
    print(f'《{movie_name}》开始下载:{datetime.now()}')
    time.sleep(randint(3,5))
    print(f'《{movie_name}》下载结束:{datetime.now()}')
t1 = Thread(target=download,args=('天若有情',))
t2 = Thread(target=download,args=('英雄本色',))
t3 = Thread(target=download,args=('使徒行者',))

t1.start()
t2.start()
t3.start()

#阻塞线程,待t1,t2,t3结束后再执行print()
t1.join()
t2.join()
t3.join()
print('所有电影下载完成!')

多进程

import time
from datetime import  datetime
from multiprocessing import Process,current_process
from random import randint
def download(movie_name:str):
    print(f'《{movie_name}》开始下载:{datetime.now()}')
    print(current_process())
    time.sleep(randint(3,5))
    print(f'《{movie_name}》下载结束:{datetime.now()}')

多进程必须写在 _ name_ = = ’ _ main_’

if __name__ == '__main__':
    p1 = Process(target=download,args=('天若有情',))
    p2 = Process(target=download,args=('英雄本色',))
    p3 = Process(target=download,args=('使徒行者',))

    p1.start()
    p2.start()
    p3.start()

多线程数据返回问题

from threading import Thread,current_thread

结论:在子线程中调用的函数如果有返回,这个返回值无法在任何地方获取

data = []

def download(name):
    print('*****')
    data.append(f'{name}')   #收集数据

def use_data():
    for x in data:
        print(f'使用{current_thread()}:{x}')

thread1 = Thread(target=download,args=('使徒行者',))
thread2 = Thread(target=download,args=('英雄本色',))
thread3 = Thread(target=use_data)

thread1.start()
thread2.start()
thread3.start()

thread1.join()
thread2.join()
thread3.join()

print(data)
线程间通信(线程间数据交流)
from threading import Thread,Lock,RLock
import time

同一个进程中的多个线程间的数据可以直接相互使用

data = 100
list1 = []

def func1():
    time.sleep(2)
    global data
    print(data)
    data = 999
    list1.append(200)

thread = Thread(target=func1)
thread.start()

# thread.join() #100
# 999 [200]
print(data,list1)   #100 [] 100

time.sleep(5)
线程间通信安全

加锁:一个数据一个锁(数据和锁需要对应)

1)、创建锁对象,保证一个数据一个锁

lock对象:锁对象.acquire() — 加锁;

​ 锁对象.release() — 释放锁

RLock对象: — 自动解锁

​ with 锁对象:

​ 操作数据的代码段

lock = Lock()

lock2 = RLock()

def save_money(num:int):
# 2、在使用需要锁的数据之前加锁
    lock.acquire()
    global balance
    b1 = balance
    time.sleep(2)
    balance = b1 + num
# 3、数据使用完后释放锁(解锁)
    lock.release()
    print(f'当前账户余额为:{balance}')

def draw_money(num:int):
    lock.acquire()
    global balance
    b1 = balance
    if(b1 > num):
        time.sleep(2)
        balance = b1 - num
    else:
        print('余额不足!')
    print(f'当前账户余额为:{balance}')
    lock.release()

thread_s = Thread(target=save_money,args=(2000,))
thread_d = Thread(target=draw_money,args=(3000,))

thread_s.start()
thread_d.start()

多线程图片爬取

import requests
from lxml import etree
from threading import Thread

def get_one_page(page: int):
    print(f'获取第{page}页数据')
    if page == 1:
        url = 'https://pic.netbian.com/index.html'
    else:
        url = f'https://pic.netbian.com/index_{page}.html'
    response = requests.get(url)
    response.encoding = 'gbk'
    html = etree.HTML(response.text)
    result = html.xpath('//div[@class="slist"]/ul/li/a/img/@src|//div[@class="slist"]/ul/li/a/span/img/@src')
    # return ['https://pic.netbian.com'+x for x in result]

    # 一页图片用一个线程来下载
    t = Thread(target=download_page_image, args=(['https://pic.netbian.com'+x for x in result],))
    t.start()
    
    # 一张图片一个线程
    # for url in ['https://pic.netbian.com'+x for x in result]:
    # t = Thread(target=download_image, args=(url,))
    # t.start()

def get_all_data():
    for page in range(1, 11):
        t = Thread(target=get_one_page, args=(page, ))
        t.start()

def download_page_image(urls: list):
    print(f'下载一页数据:{len(urls)}')
    for url in urls:
        download_image(url)

def download_image(url: str):
    response = requests.get(url)
    f = open(f'files/{url.split("/")[-1]}', 'wb')
    f.write(response.content)
    # print('下载完成!')

get_all_data()

相关文章