当前位置: 首页 > news >正文

【python实操】马上毕业了,你还不懂什么是守护线程、线程、进程?(附12306抢票程序-源代码)

  • 作者:20岁爱吃必胜客(坤制作人),近十年开发经验, 跨域学习者,目前于海外某世界知名高校就读计算机相关专业。
  • 荣誉:阿里云博客专家认证、腾讯开发者社区优质创作者,在CTF省赛校赛多次取得好成绩。
  • 跨领域学习,喜欢摄影、弹吉他、咏春拳。文章深入浅出、语言风趣;爱吃必胜客社区创立者,旨在“发现美 欣赏美

  • 🏆 学习系列专栏
    。🏅 Python学习宝库
    。🏅 网络安全学习宝库

在这里插入图片描述

在这里插入图片描述

文章目录

  • ⭐️前言
    • 🌟 进程-process
    • 🌟 线程-Treading
    • 🌟 守护线程-Deamon Treading
      • ☀️java中的应用
      • ☀️python中的应用
  • ⭐️多线程模块threading
    • 🌟 使用锁来保护共享资源的访问
  • ⭐️queue模块
  • ⭐️多线程购买火车票的代码
  • ⭐️12306抢票程序-源代码

⭐️前言

下面我们先回顾基础知识,分别是:

在这里插入图片描述

进程-process
线程-Treading
守护线程-Deamon Treading

🌟 进程-process

进程就是一个装线程的容器
是线程的容器

进程就是一个个正在运行的独立软件

🌟 线程-Treading

又叫Light Weight Process LWP
轻量级进程
程序执行流的最小单元

线程组成:

线程id-----当前指令指针------寄存器集合---------堆栈

🌟 守护线程-Deamon Treading

☀️java中的应用

守护线程(Daemon Thread)是一种特殊的线程,其生命周期与 Java 虚拟机(JVM)的生命周期相同。当 JVM 中已不存在任何非守护线程时,虚拟机会自动退出,守护线程也会随之结束。因此,守护线程也被称为“服务线程”或“后台线程”

守护线程主要用于执行一些低优先级的任务,比如垃圾回收、内存管理、日志维护等工作。它通常不干扰其他线程的执行,当所有非守护线程执行结束后,它会被自动中断。

在 Java 中创建守护线程的方法是通过 Thread 类的 setDaemon() 方法,将线程设置为守护线程。当线程启动后,也可以使用 isDaemon() 方法来检查该线程是否为守护线程

需要注意的是,守护线程和非守护线程的区别在于它们的执行权限守护线程不能访问程序中的非守护线程或共享资源。因此,在使用守护线程时需要仔细考虑线程之间的依赖关系和共享资源的使用。

☀️python中的应用

Python中也有守护线程的概念,它与Java中的作用是相似的。在Python中,可以通过Thread类中的setDaemon方法来将线程设置为守护线程

在Python中,守护线程通常用于执行一些低优先级的任务或后台服务,例如监控另一个线程是否终止、自动保存数据等。当所有非守护线程结束时,守护线程也会自动结束,不会阻塞主进程的结束,这在一些长时间运行的程序中非常有用

需要注意的是,守护线程并不是万能的解决方案,它不能处理复杂的计算任务和涉及共享资源的并发问题。此外,在使用守护线程时需要仔细考虑线程之间的依赖关系和共享资源的使用,以避免数据竞争和死锁等问题。

下面是一个简单的例子,展示了如何在Python中创建守护线程:

import threading
import time

def print_time():
    for i in range(5):
        print("Current time:", time.ctime())
        time.sleep(1)

t = threading.Thread(target=print_time)
t.daemon = True # 将线程设置为守护线程
t.start()

print("Main process end.")

在上面的代码中,创建了一个名为print_time的函数,通过time模块打印当前时间,并在每次打印后等待1秒。然后创建了一个线程对象t,将print_time函数作为其目标函数,然后将线程设置为守护线程并启动。最后主进程打印一条结束信息。

⭐️多线程模块threading

在这里插入图片描述

现在用threading取代了thread模块。
Python中的threading模块提供了一种方便的方式来创建和管理线程。下面是使用threading模块创建和管理线程的示例代码:

import threading

# 定义一个线程执行的任务函数
def task():
    print("This is a task function.")
    
# 创建一个新线程
t = threading.Thread(target=task)

# 启动该线程
t.start()

# 等待该线程结束
t.join()

print("Main thread ends.")

在上面的代码中,首先定义了一个task()函数,用于表示线程执行的具体任务。然后通过threading.Thread()创建一个新的线程对象t,并将task函数作为其目标函数。最后启动该线程并等待其运行结束,然后主线程继续执行。

除了这个基本的线程创建和启动方式,threading模块还提供了一些方便的功能,例如:

  • threading.current_thread():返回当前线程对象;
  • threading.active_count():返回当前活跃的线程数;
  • threading.enumerate():返回当前所有活跃线程的列表;
  • threading.Lock():创建一个锁对象,用于保护共享资源的互斥操作;
  • threading.Event():创建一个事件对象,用于线程之间的通信和同步等。

🌟 使用锁来保护共享资源的访问

在这里插入图片描述

在这里插入图片描述

下面是一个例子,演示如何使用锁来保护共享资源的访问

import threading

# 定义一个共享变量
num = 0

# 创建一个锁对象
lock = threading.Lock()

# 定义一个线程执行的任务函数
def task():
    global num
    for i in range(1000000):
        lock.acquire()
        num += 1
        lock.release()

# 创建多个线程
threads = [threading.Thread(target=task) for i in range(10)]

# 启动这些线程
for t in threads:
    t.start()

# 等待这些线程结束
for t in threads:
    t.join()

print("Final result:", num)

在上面的代码中,首先定义了一个共享变量num,它的值将被多个线程共同更新。然后创建一个锁对象lock,用于保护num变量的访问。接下来定义一个task()函数。

⭐️queue模块

实现多生产者,多消费者队列
该技术是多线程安全共享数据的最佳选择技术之一。

⭐️多线程购买火车票的代码

以下是一个多线程购买火车票的代码示例:

import threading
import time

class Ticket:
    def __init__(self, name, quantity):
        self.name = name
        self.quantity = quantity
        self.lock = threading.Lock()
        
    def buy(self, num):
        self.lock.acquire()
        try:
            if self.quantity >= num:
                self.quantity -= num
                print(f"当前剩余车票:{self.quantity}")
                return True
            else:
                print("余票不足,购票失败")
                return False
        finally:
            self.lock.release()

def worker(name, ticket, num):
    while True:
        if ticket.buy(num):
            print(f"{name} 购买 {num} 张车票成功!")
            break
        else:
            print(f"{name} 正在尝试购买车票中...")
            time.sleep(1)

if __name__ == '__main__':
    t = Ticket("北京-上海", 10)
    threads = []
    for i in range(5):
        name = f"用户{i+1}"
        num = 2
        t1 = threading.Thread(target=worker, args=(name, t, num))
        threads.append(t1)
    for t in threads:
        t.start()
    for t in threads:
        t.join()

代码说明:

  1. Ticket 类表示火车票,包括票务名称和余票数量,以及一个锁对象用于线程同步。
  2. buy 方法用于购买车票,传入要购买的数量,如果余票足够则减少对应数量的余票,并返回 True,否则返回 False。
  3. worker 函数表示一个购票线程,不断尝试购买车票,如果成功则输出购买成功的信息,退出循环;否则等待 1 秒后重试。
  4. 在程序运行时创建 5 个购票线程,每个线程购买 2 张车票。线程启动后并发执行购票任务,直到所有线程购买成功为止。

注意:在实际开发中,购买车票需要连接网络、进行 IO 操作,并且可能会遇到一些异常情况,因此需要添加异常处理等相关代码来保证程序的稳定性和健壮性。

⭐️12306抢票程序-源代码

抢火车票需要模拟登陆、查询余票、提交订单等步骤,比较复杂,建议使用官方提供的API。以下是一个简单的模拟查询余票的Python代码示例。

import urllib.request
import json
import time

# 设置参数
fromStation = '北京'
toStation = '上海'
departureDate = '2022-01-01'

# 利用API查询余票
url = 'https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date={}&leftTicketDTO.from_station={}&leftTicketDTO.to_station={}&purpose_codes=ADULT'.format(departureDate, fromStation, toStation)
request = urllib.request.Request(url)
# 添加请求头
request.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3')
response = urllib.request.urlopen(request)
result = response.read().decode('utf-8')
data = json.loads(result)

# 解析结果
trains = data['data']['result']
for train in trains:
    trainList = train.split('|')
    if trainList[0] == '':  # 过滤无效车次
        continue
    if trainList[29] == 'Y':  # 判断是否有余票
        print('车次:{},余票数量:{}'.format(trainList[3], trainList[29]))

# 设置查询间隔
time.sleep(10)

需要注意的是,12306官方接口的调用频率有限制,需要合理控制查询间隔,防止被封IP。此外,需要获取验证码和自动提交订单等功能可以使用第三方库,比如PyAutoGUI。但是使用自动化脚本抢票有一定风险,建议谨慎操作。
在这里插入图片描述

相关文章:

  • Springboot整合rabbitmq并实现消息可靠性和持久性
  • ChatGPT可以作为一个翻译器吗?
  • 一文学会 Spring MVC 表单标签
  • 【联邦学习(Federated Learning)】- 横向联邦学习与联邦平均FedAvg
  • 免费一键生成原创文章-原创文章批量生成
  • 众人围剿,GPT-5招惹了谁
  • Spring Boot 3.0系列【19】核心特性篇之自定义Starter启动器
  • oracle中sql 正则怎么写?
  • 【5G RRC】NR测量Gap介绍
  • 【T+】登录畅捷通T+软件后提示同一个浏览器中不允许存在用户XX同时在线。
  • pom文件详解
  • JVM 类加载器子系统
  • 半小时内实现Esp32-Cam模型训练和图像识别
  • 关于一个大学生写一个题目写一天
  • 【C#进阶】C# 多线程
  • [ 一起学React系列 -- 8 ] React中的文件上传
  • 《Javascript高级程序设计 (第三版)》第五章 引用类型
  • 【技术性】Search知识
  • C++入门教程(10):for 语句
  • css系列之关于字体的事
  • JavaScript设计模式与开发实践系列之策略模式
  • 不上全站https的网站你们就等着被恶心死吧
  • 计算机在识别图像时“看到”了什么?
  • 京东美团研发面经
  • 前端每日实战:61# 视频演示如何用纯 CSS 创作一只咖啡壶
  • 前端性能优化--懒加载和预加载
  • 使用Swoole加速Laravel(正式环境中)
  • 微信端页面使用-webkit-box和绝对定位时,元素上移的问题
  • 硬币翻转问题,区间操作
  • 中文输入法与React文本输入框的问题与解决方案
  • 1.Ext JS 建立web开发工程
  • 如何用纯 CSS 创作一个货车 loader
  • ​​​​​​​ubuntu16.04 fastreid训练过程
  • ​一些不规范的GTID使用场景
  • #HarmonyOS:软件安装window和mac预览Hello World
  • #if和#ifdef区别
  • (16)Reactor的测试——响应式Spring的道法术器
  • (done) 两个矩阵 “相似” 是什么意思?
  • (iPhone/iPad开发)在UIWebView中自定义菜单栏
  • (Spark3.2.0)Spark SQL 初探: 使用大数据分析2000万KF数据
  • (ZT)出版业改革:该死的死,该生的生
  • (二十四)Flask之flask-session组件
  • (附源码)node.js知识分享网站 毕业设计 202038
  • (理论篇)httpmoudle和httphandler一览
  • (力扣记录)1448. 统计二叉树中好节点的数目
  • (七)MySQL是如何将LRU链表的使用性能优化到极致的?
  • (十)c52学习之旅-定时器实验
  • (自适应手机端)响应式新闻博客知识类pbootcms网站模板 自媒体运营博客网站源码下载
  • .NET Core 版本不支持的问题
  • .NET处理HTTP请求
  • .net使用excel的cells对象没有value方法——学习.net的Excel工作表问题
  • @ModelAttribute注解使用
  • [HXPCTF 2021]includer‘s revenge
  • [IOI2007 D1T1]Miners 矿工配餐
  • [java后端研发]——文件上传与下载(2种方式)