栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

第七天 线程安全和队列

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

第七天 线程安全和队列

1. 进程和线程

进程:一个正则运行的应用程序就是一个进程;每一个进程会运行在其专用且受保护的内存空间中。
线程:线程是执行任务的基本单位
每一个进程至少要有一个线程(默认只有一个)
单线程串行:如果要在一个线程中执行多个任务,任务是按顺序一个一个的执行

进程 - 车间; 线程 - 工人

2. 多线程

多线程:在一个进程中创建多个线程;执行在多个线程中的任务,可以并发(同时)执行
多线程原理:利用cpu空闲时间干活

使用多线程方法一:直接创建Thread类的对象
线程对象 = Thread(target=函数, args=元组)

使用多线程方法二:创建Thread的子类对象
class 子类(Thread):
def init(self):
super().init()
实现任务功能需要额外的数据对应的属性

def run(self):
    需要在子线程中执行的任务
3.多进程

多进程:一个程序可以有多个进程(默认只有一个)

创建多进程的方法:
进程对象 = Process(target=函数, args=元组)

from multiprocessing import Process

4. 等待(阻塞) - join

线程对象/进程对象.join()
指定线程任务/进程任务完成后才执行的操作

有数据安全的代码
from random import randint
import time
from threading import Thread

# 数据安全问题
"""
如果一个线程将数据读出来,修改后还没来得及写入,另外一个线程又对这个数据进行了读写操作,那么这个
时候就可能会出现数据安全问题
"""
# 解决方法:加锁


def save_money(money):
    global account
    x = account
    time.sleep(randint(2, 5))
    account = x + money
    print(f'存钱{money}元,余额:{account}')


def draw_money(money):
    global account
    x = account
    if x >= money:
        time.sleep(randint(2, 5))
        account = x - money
        print(f'取{money}元,余额:{account}')
    else:
        print('余额不足!')


if __name__ == '__main__':
    # 账户余额
    account = 2000

    t1 = Thread(target=save_money, args=(20000,))
    t2 = Thread(target=draw_money, args=(1000,))

    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(f'余额:{account}')

加锁解决安全问题
from random import randint
import time
from threading import Thread, Lock

# 数据安全问题
"""
如果一个线程将数据读出来,修改后还没来得及写入,另外一个线程又对这个数据进行了读写操作,那么这个
时候就可能会出现数据安全问题
"""
# 解决方法:加锁
"""
1. 保证一个公用数据一把锁(创建一个锁对象): Lock()
2. 在某个线程获取公用数据前加锁: 锁对象.acquire()
3. 在数据写入或释放锁(解锁): 
"""


def save_money(money):
    print(f'存:{money}元')
    global account
    # 在获取数据前加锁
    account_lock.acquire()

    x = account
    time.sleep(randint(2, 5))
    account = x + money

    # 数据写入后解锁
    account_lock.release()


def draw_money(money):
    print(f'取:{money}元')
    global account

    # 在获取数据前加锁
    account_lock.acquire()

    x = account
    if x >= money:
        time.sleep(randint(2, 5))
        account = x - money

        # 数据写入后解锁
        account_lock.release()
    else:
        print('余额不足!')


if __name__ == '__main__':
    # 账户余额
    account = 2000
    # 一个数据对应一个锁
    account_lock = Lock()

    t1 = Thread(target=save_money, args=(20000,))
    t2 = Thread(target=draw_money, args=(1000,))

    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(f'余额:{account}')

RLOCK锁 数据安全问题

如果一个线程将数据读出来,修改后还没来得及写入,另外一个线程又对这个数据进行了读写操作,那么这个
时候就可能会出现数据安全问题

解决方法:加锁
    一个数据对应一个锁对象:RLock()将数据操作放到锁指定的区域
    with 锁对象:
    操作数据
from random import randint
import time
from threading import Thread, RLock


def save_money(money):
    print(f'存:{money}元')
    global account
    with account_lock:
        x = account
        time.sleep(randint(2, 5))
        account = x + money


def draw_money(money):
    print(f'取:{money}元')
    global account
    with account_lock:
        x = account
        if x >= money:
            time.sleep(randint(2, 5))
            account = x - money
        else:
            print('余额不足!')


if __name__ == '__main__':
    # 账户余额
    account = 2000
    account_lock =RLock()

    t1 = Thread(target=save_money, args=(20000,))
    t2 = Thread(target=draw_money, args=(1000,))

    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(f'余额:{account}')

1.多线程数据收集

如果多个线程的任务产生了不同的数据,想要将这些数据收集到一起的方法:
方法1:定义一个全局的普通容器(例如:列表、字典、元组)
方法2:线程队列

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


def download(name):
    print(f'{name}开始下载:{datetime.now()}')
    time.sleep(randint(2, 7))
    print(f'{name}下载完成:{datetime.now()}')
    # print(f'{name}数据')

    global all_data
    all_data.append(f'{name}数据')


# 需求1:在主线程中收集每个子线程得到的数据
# 方法:定义全局的一个列表, 将子线程中产生的数据作为列表元素添加到列表中
if __name__ == '__main__':
    all_data = []

    ts = []
    for x in range(1, 11):
        t = Thread(target=download, args=(f'电影{x}',))
        t.start()
        ts.append(t)

    for t in ts:
        t.join()
    print(all_data)

1. 线程队列

队列: 队列是一种先进先出的容器型数据结构,Python中的线程队列和进程队列都是数据安全。
用法: 1)导入队列类型: from queue import Queue
2)创建队列对象: Queue() / Queue(最大元素个数)
3)操作队列: a. 进: 队列对象.put(数据)
b. 出: 队列对象.get() / 队列对象.get(timeout=超时时间)

注意: 队列的get操作,在队列是空队列的时候会等待(不会报错),等到队列中有数据的时候会马上获取数据。
如果设置了超时时间,超时会报错!

from datetime import datetime
import time
from random import randint
from threading import Thread
from queue import Queue 


def download(name):
    print(f'{name}开始下载:{datetime.now()}')
    time.sleep(randint(2, 7))
    print(f'{name}下载完成:{datetime.now()}')
    # print(f'{name}数据')
    q.put(f'{name}数据')


if __name__ == '__main__':
    q = Queue()

    for x in range(1, 11):
        t = Thread(target=download, args=(f'电影{x}',))
        t.start()

    # 需求2:即时处理子线程获取的所有数据
    for _ in range(10):
        result = q.get()
        print(f'处理:{result}')
    print('所有数据处理完成!')
线程队列实际问题
from datetime import datetime
import time
from random import randint
from threading import Thread
from queue import Queue


def download(name):
    print(f'{name}开始下载:{datetime.now()}')
    time.sleep(randint(2, 7))
    print(f'{name}下载完成:{datetime.now()}')
    q.put(f'{name}数据')


def get_data():
    while True:
        result = q.get()
        if result == 'end':
            break
        print(f'处理:{result}')


if __name__ == '__main__':
    q = Queue()

    ts = []
    for x in range(1, randint(6, 11)):
        t = Thread(target=download, args=(f'电影{x}',))
        t.start()
        ts.append(t)

    deal_t = Thread(target=get_data)
    deal_t.start()

    for t in ts:
        t.join()
    q.put('end')

进程队列
# 1. 进程间的数据共享
# 进程间进行数据共享出来使用进程队列以外,没有别的方法

from multiprocessing import Process, Queue as P_Queue
from random import randint
from queue import Queue


def func1(q2):
    result = randint(0, 100)
    # 3. 在子进程中通过参数来使用全局的进程队列保存数据
    q2.put(result)
    print('数据1添加成功!')


def func2(q2):
    result = randint(1000, 2000)
    # 3. 在子进程中通过参数来使用全局的进程队列保存数据
    q2.put(result)
    print('数据2添加成功!')


if __name__ == '__main__':
    # 1. 创建一个全局的进程队列
    q = P_Queue()

    # 2.将进程队列作为参数传递到子进程中
    p1 = Process(target=func1, args=(q,))
    p2 = Process(target=func2, args=(q,))
    p1.start()
    p2.start()

    p1.join()
    p2.join()

    # 4. 在主进程中直接获取进程队列中的数据(如果进程队列要在子进程中使用,必须通过参数传递,不能直接用)
    print(q.get())
    print(q.get())

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/709321.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号