Thread
创建Thread方式
1 实例化Thread类创建线程
Thread类对象属性:
name :线程名。
ident :线程标识符。
daemon :布尔标志,表示这个线程是否是守护线程。
Thread类的构造方法的参数如下:
Thread(self,group=None,target=None,name=None,args=(),kwargs={},*,daemon=None)
group :默认为None,该参数不必理会。
target :指定线程要调用的方法或函数。
name :指定线程的别名。
args :指定target所指向的方法或者函数要用到的位置参数。
kwargs :指定target所指向的方法或者函数要用到的关键字参数。
daemon :指定线程是否为守护线程。
示例:
1 | import threading |
运行结果:
1 | thread MainThread is running. |
<分析>
这个例子比较简单,只是简单的展示了创建线程的方法。每一个进程都有一个主线程,主线程是默认启动的,于是可以看到我们只实例化Thread类创建了一个线程,但运行结果却有两个线程。
另外可以发现每一个线程都有一个name,比方说MainThread(主线程)、Thread-1,这个name是哪来的呢?threading模块内有一个current_thread()函数,该函数总是返回当前线程的实例,那么current_thread().name不就是返回当前线程实例的name了么。另外,主线程默认的name就是MainThread,子线程如果没有在创建时指定,那么默认就是Thread-1、Thread-2…以此类推。
另外,启动一个线程的start()方法、用以表示现线程活动的run()方法、等待一个线程结束的join(timeout)方法,判断线程是否”存活”的is_alive()方法…这些方法都和进程中的这些方法完全类似,鉴于有关进程我已经在前两篇介绍过,这里就不再细说。
唯一提一点就是,进程中关于强制终止进程的terminate()方法不适用于Thread对象,唯独这一个不要在线程中去用。
最后,threading模块下除current_thread()之外的其他常用函数:
- threading.enumerate() :返回一个包含正在运行的线程的list。
- threading.activeCount() :返回正在运行的线程数量。等价于len(threading.enumerate())。
Thread类的一些其它方法:
getName() : 返回线程名。
setName() :设置线程名。
isDaemon() :判断该线程是否是守护线程,是返回True。
setDaemon() :设置线程的thread.daemon属性,该方法必须在线程start()之前调用。
以上方法都已经不常用,在Python的新版本中,设定name或者daemon属性可以不借助方法直接设定。
2 并发线程
进程和线程都可以并发执行,但线程由于划分尺度小,因此并发性相比于进程是要高的;另外,创建线程要更加简单,也更易于维护,由于多个线程共享进程的内存空间,这也使得线程更易于通信;除此之外,线程是Python直接支持的,这也简化了多线程的编程。
Python中的多线程也并不全是优点,事实上,Python中的多线程只是表面上的多线程,它和C++或者java中的不同,由于全局解释器锁GIL的存在,Python在一个时刻只会有一个线程在运行,这只是并发而不是并行。所以同样是多线程,java可以充分利用CPU的多核,但Python自始至终只会用到一个核,这就像单核CPU系统的多进程一样,Python的多线程是名不副实的。
1 | import threading |
3.继承Thread类创建线程
通过继承Thread,重写Thread类的run()方法得到Thread类的子类,实例化子类也能创建线程。
1 | import threading |
如上,通过重写Thread类的run()方法,就可以在接下来直接使用Thread的子类创建一个线程,当然也可以重写Thread类的init()方法,得到一个令人满意、功能完备的Thread类的子类。这种创建线程的方式更加适合面向对象编程。
4. 实例化Thread,传入一个类实例
我们一般更倾向于使用上面介绍的两种方法创建线程,在第一种方法中,我们实例化Thread类,并传给它一个函数,第二种方法通过继承Thread得到子类创建线程,而接下来介绍的第三种方法,你会发现传入一个可调用的类实例同样可以创建线程。
这种创建线程的方法更加接近于面向对象的多线程编程,相比于传入函数而言具有更高的灵活性。
1 | import threading |
START!
thread Thread-1 is running >> 0.
thread Thread-2 is running >> 1.
thread Thread-3 is running >> 2.
thread Thread-1 end.
thread Thread-2 end.
thread Thread-3 end.
ALL END!
<分析>
这种创建线程的方法比起前两种应该稍微复杂了一点,但实际上也就是相比于传入函数改为传入一个类,类完全由我们自己定制,构造方法init()可以添加任何我们需要的参数,之后注意这个特殊方法call(),它调用传入类的函数,并使用我们传入类的参数。
如果我们力求逻辑清晰地快速创建一个线程,任务要求不高,我们倾向于使用第一种创建线程的方法;如果我们需要完全面向对象的多线程编程,我们倾向于使用继承Thread类、实例化子类创建线程的方法,所以这第三种方法的地位有点尴尬,它并不被常用,这里了解一下知道就好。
ref:
Python 三种方法创建线程_python创建线程-CSDN博客
asyncio
asyncio
是 Python 中的一个异步 I/O 框架,它支持编写并发代码使用协程、多路复用 I/O 访问操作以及高级别并发原语(如事件循环、锁、条件变量等)。asyncio
使得 Python 能够高效地处理 I/O 密集型任务,如网络请求、文件 I/O 等
asyncio 的基本概念
事件循环(Event Loop):事件循环是
asyncio
的核心组件,它调度协程(coroutines)的执行。事件循环负责管理回调函数的注册和调度。协程(Coroutines):协程是一种特殊的生成器函数,它可以暂停执行并恢复。协程在事件循环中被调度执行。
Future:
Future
是一个特殊类型的对象,代表了将来某个时刻可能会得到的结果。Future
对象可以用来挂起协程的执行,直到结果可用为止。Task:
Task
是Future
的子类,用于封装协程的执行。通过asyncio.create_task()
或loop.create_task()
创建Task
对象。异步函数(Async Functions):定义以
async def
开头的函数称为异步函数。异步函数可以使用await
关键字来等待另一个协程的完成。
asyncio 的基本用法
创建事件循环
1 | import asyncio |
使用 async
和 await
1 | import asyncio |
创建任务
1 |