⭐️前言
下面我们先回顾基础知识,分别是:
进程-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()
代码说明:
Ticket
类表示火车票,包括票务名称和余票数量,以及一个锁对象用于线程同步。buy
方法用于购买车票,传入要购买的数量,如果余票足够则减少对应数量的余票,并返回 True,否则返回 False。worker
函数表示一个购票线程,不断尝试购买车票,如果成功则输出购买成功的信息,退出循环;否则等待 1 秒后重试。- 在程序运行时创建 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。但是使用自动化脚本抢票有一定风险,建议谨慎操作。