Python 多线程 使用线程 (二)

Python中实现多线程需要使用到 threading 库,其中每一个 Thread类 的实例控制一个线程。

Thread类

#类签名

def __init__(self, group=None, target=None, name=None,
                 args=(), kwargs=None, *, daemon=None):

  简单介绍一些初始化参数:

target: 指定线程由 run () 方法调用的可调用对象。默认为 None, 意味着不调用任何内容。

name: 指定该线程的名称。 在默认情况下,创建一个唯一的名称。

args: target调用的实参,元组格式。默认为 (),即不传参。

daemon: 为False表示父线程在运行结束时需要等待子线程结束才能结束程序,为True则表示父线程在运行结束时,子线程无论是否还有任务未完成都会跟随父进程退出,结束程序。

线程启动:

import threading

def worker(arg):#线程执行的目标函数
    print("I‘m working {}".format(arg))
    print("Fineshed")

t = threading.Thread(target=worker,args=(threading.current_thread(),),name="firstworker")#线程对象
t.start()#启动线程

运行结果:
I‘m working <_MainThread(MainThread, started 10936)>
Fineshed

  上面例子中,当函数执行完之后,线程也就跟着退出了。

线程的传参:

import threading

def add(x,y):
    print(x+y)

t = threading.Thread(target=add,args=(4,5))
t.start()

print("====end===")
运行结果:
9
====end===

  线程的传参和函数传参没有区别,只需要注意传入的必须为元祖格式。

线程退出:

如果线程中任务是无限循环语句,那这个线程将无法自动停止。

Python线程退出条件有以下几种:

1、线程内的函数语句执行完毕,线程自动结束

2、线程内的函数抛出未处理的异常

import threading
import time

def worker(arg):
    while True:
        time.sleep(1)
        print("I‘m working {}".format(arg))
    print("Fineshed")

t = threading.Thread(target=worker,args=(threading.current_thread(),),name="firstworker")
t.start()
运行结果:
I‘m working <_MainThread(MainThread, stopped 2468)>
I‘m working <_MainThread(MainThread, stopped 2468)>
I‘m working <_MainThread(MainThread, stopped 2468)>
...

  上面例子中,线程启动后,将一直循环下去,线程不会自动退出。

import threading
import time

def worker(arg):
    count = 0
    while True:
        if count > 5:
            raise RuntimeError(count)
        time.sleep(1)
        print("I‘m working {}".format(arg))
        count += 1
    print("Fineshed")

t = threading.Thread(target=worker,args=(threading.enumerate(),))
t.start()

print("====end===")

运行结果:
====end===
I‘m working [<_MainThread(MainThread, stopped 10992)>]
I‘m working [<_MainThread(MainThread, stopped 10992)>]
I‘m working [<_MainThread(MainThread, stopped 10992)>]
I‘m working [<_MainThread(MainThread, stopped 10992)>]
I‘m working [<_MainThread(MainThread, stopped 10992)>]
I‘m working [<_MainThread(MainThread, stopped 10992)>]
Exception in thread Thread-1:
Traceback (most recent call last):
  File "C:/python/test.py", line 8, in worker
    raise RuntimeError(count)
RuntimeError: 6

  上面例子中,演示了触发异常自动退出线程。但最先打印的是主程序的"===end==="语句,是因为在程序中,主线程启动一个线程后,不会等待子线程执行完毕,就继续执行了后续语句,在执行完主线程语句后,发现还有子线程没有结束,于是等待子线程执行结束,子线程在运行时抛出了未处理的异常,最终子线程结束,主线程也随之结束。这里需要了解daemon线程和non-daemon线程,稍后就会介绍。

threading属性:

threading.current_thread()   返回当前线程对象threading.main_thread()      返回主线程对象threading.active_count()     返回处于Active状态的线程个数threading.enumerate()        返回所有存活的线程的列表,不包括已经终止的线程和未启动的线程threading.get_ident()        返回当前线程的ID,非0整数

举例:

import threading
import time

def showthreadinfo():
    print("current thread = {}".format(threading.current_thread()))
    print("main thread  = {}".format(threading.main_thread()))
    print("active thread count = {}".format(threading.active_count()))
    print("active thread list = {}".format(threading.enumerate()))
    print("thread id = {}".format(threading.get_ident()))
    print("~~~~~~~~~~~~~")

def add(x,y):
    time.sleep(1)
    showthreadinfo() #子线程中调用
    print(x+y)

showthreadinfo() #主线程中调用
time.sleep(1)

t = threading.Thread(target=add,args=(4,5))
t.start()

print("====end===")

运行结果:
current thread = <_MainThread(MainThread, started 192)>
main thread  = <_MainThread(MainThread, started 192)>
active thread count = 1
active thread list = [<_MainThread(MainThread, started 192)>]
thread id = 192
~~~~~~~~~~~~~
====end===
current thread = <Thread(Thread-1, started 8424)>
main thread  = <_MainThread(MainThread, stopped 192)>
active thread count = 2
active thread list = [<_MainThread(MainThread, stopped 192)>, <Thread(Thread-1, started 8424)>]
thread id = 8424
~~~~~~~~~~~~~
9

  上面例子中,在主线程中只能看到存活的只有自己,因为子线程还没有启动,且它的父线程就是它自己。子线程启动时,它的名字为Thread-1,这个名字是解释器自动命名的,如果定义线程对象时添加了name="threadName",则这里显示的就是threadName;同时,子线程的父线程就是主线程,也就是说谁启动的线程谁就是它的父线程;子线程能看到的存活线程有父线程和自身。

Thread实例的属性:

threading.current_thread().name        线程名,只是一个标识符,可以使用getName()、setName()获取和运行时重命名。threading.current_thread().ident       线程ID,非0整数。线程启动后才会有ID,否则为None。线程退出,此ID依旧可以访问。此ID可以重复使用threading.current_thread().is_alive()  返回线程是否存活,布尔值,True或False。

举例:

import threading
import time

def worker():
    count = 1
    while True:
        if count >= 6:
            break
        time.sleep(1)
        count += 1
        print("thread name = {}".format(threading.current_thread().name))

t = threading.Thread(target=worker,name="MyThread")
t.start()

while True:
    time.sleep(1.1)
    if t.is_alive():
        print("{} {} alive".format(t.name,t.ident))
    else:
        print("{} {} alive".format(t.name, t.ident))
        t.start()

print("====end===")

运行结果:
thread name = MyThread
MyThread 9400 alive
thread name = MyThread
MyThread 9400 alive
thread name = MyThread
MyThread 9400 alive
thread name = MyThread
MyThread 9400 alive
thread name = MyThread
MyThread 9400 alive
Traceback (most recent call last):
  File "C:/python/test.py", line 22, in <module>
    t.start()
    raise RuntimeError("threads can only be started once")
RuntimeError: threads can only be started once

  从上面例子中可以看到子线程存活时的名字和线程ID,但在线程退出后,尝试再次启动线程时,抛出RuntimeError异常,表明线程对象在定义后只能启动一次。

举例 getName()和setName():

import threading
import time

def add(x,y):
    for _ in range(5):
        time.sleep(1)
        print("x+y={}".format(x+y))

t = threading.Thread(target=add,name="MyThread",args=(6,7))
t.start()

while True:
    time.sleep(1)
    if t.is_alive():
        print("{} {} alive".format(t.name,t.ident))
        print("Thread name",t.getName())
        t.setName("MyThreadTwo")
    else:
        print("{} {} alive".format(t.name, t.ident))
        print("Thread abort....")
        break
        # t.start()

print("====end===")

运行结果:
MyThread 2564 alive
Thread name MyThread
x+y=13
MyThreadTwo 2564 alive
Thread name MyThreadTwo
x+y=13
MyThreadTwo 2564 alive
Thread name MyThreadTwo
x+y=13
MyThreadTwo 2564 alive
Thread name MyThreadTwo
x+y=13
MyThreadTwo 2564 alive
Thread name MyThreadTwo
x+y=13
MyThreadTwo 2564 alive
Thread abort....
====end===

  上面例子演示了在运行时获取线程名和重命名线程名。

线程的start()和run()方法:

start():

import threading
import time

def add(x,y):
    for _ in range(5):
        time.sleep(0.5)
        print("x+y={}".format(x+y))

class MyThread(threading.Thread):
    def start(self):
        print(‘start~~~~~~~~~~‘)
        super().start()

    def run(self):
        print(‘run~~~~~~~~~~~~‘)
        super().run()  #调用父类的start()和run()方法

t = MyThread(target=add,name="MyThread",args=(6,7))
t.start()
# t.run()
print("====end===")

运行结果:
start~~~~~~~~~~
run~~~~~~~~~~~~
====end===
x+y=13
x+y=13
x+y=13
x+y=13
x+y=13

  从上面的例子中,可以看出start()方法会先运行start()方法,再运行run()方法。

跟进一下start() 方法源码中的调用过程:

1、def start(self):
    _start_new_thread(self._bootstrap, ())
    ....

2、_start_new_thread = _thread.start_new_thread

3、def start_new_thread(function, args, kwargs=None):
    pass

4、def _bootstrap(self):
    self._bootstrap_inner()

5、def _bootstrap_inner(self):
    ....
    try:
        self.run()#最终start()方法调用了run()方法
    except SystemExit:
        pass

  从上面跟踪源码的过程大概了解了start()方法如何调用到了run()方法。

run()方法:

import threading
import time

def add(x,y):
    for _ in range(5):
        time.sleep(0.5)
        print("x+y={}".format(x+y))

class MyThread(threading.Thread):
    def start(self):
        print(‘start~~~~~~~~~~‘)
        super().start()

    def run(self):
        print(‘run~~~~~~~~~~~~‘)
        super().run()  #调用父类的start()和run()方法

t = MyThread(target=add,name="MyThread",args=(6,7))
# t.start()
t.run()
print("====end===")

运行结果:
run~~~~~~~~~~~~
x+y=13
x+y=13
x+y=13
x+y=13
x+y=13
====end===

  上面例子中,运行线程的run()方法只能调用到run()方法。

跟踪一下run() 方法在源码中的调用过程:

1、def __init__(self, group=None, target=None, name=None,
                 args=(), kwargs=None, *, daemon=None):
    self._target = target
    self._args = args
    self._kwargs = kwargs
    ....

2、def run(self):
    if self._target:
        self._target(*self._args, **self._kwargs)
    ....

  可以看出,_target是我们传入的目标函数,run()方法其实就类似一个装饰器,最终还是将_args 和_kwargs 参数传入目标函数运行,返回结果。

start() --> run() --> _target()

run() --> _target()

上面两个例子简单介绍了start()方法和run()方法的调用,下一篇文章再详细看一下它们到底有什么区别。

总结:

本文主要介绍了: Thread类、线程启动、线程的传参、线程退出、threading属性、Thread实例的属性、举例getName()和setName()、线程的start()和run()方法

时间: 2024-10-10 14:47:40

Python 多线程 使用线程 (二)的相关文章

python多线程之线程锁二(同一时间一个线程获得2把线程锁)

#coding:utf-8 '''线程锁''' import threading import time num = 0 #全局变量 num2 = 0 def runs():     time.sleep(1)     global num #在函数内部要对全局变量进行更改,需要进行声明     global num2     lock.acquire() #在操作时锁住,防止其他线程在同一时间对num变量进行加1,从而确保数据在同一时间确保只有一个线程对它进行更改,不然造成数据不正确     

Python多线程之线程创建和终止

python主要是通过thread和threading这两个模块来实现多线程支持.python的thread模块是比较底层的模块,python的threading模块是对thread做了一些封装,可以更加方便的被使用.但是python(cpython)由于GIL的存在无法使用threading充分利用CPU资源,如果想充分发挥多核CPU的计算能力需要使用multiprocessing模块. 如果在对线程应用有较高的要求时可以考虑使用Stackless Python来完成.Stackless Py

python基础(34):线程(二)

1. python线程 1.1 全局解释器锁GIL Python代码的执行由Python虚拟机(也叫解释器主循环)来控制.Python在设计之初就考虑到要在主循环中,同时只有一个线程在执行.虽然 Python 解释器中可以“运行”多个线程,但在任意时刻只有一个线程在解释器中运行.对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行. 在多线程环境中,Python 虚拟机按以下方式执行: 设置 GIL 切换到一个线程去运行 运行指定数量的字节码指令或

python 多线程和线程池

1 代码Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 1 #coding:utf-8 2 3 #Python的线程池实现 4 5 import Queue 6 import threading 7 import sys 8 import time 9 import urllib 10 11 #替我们工作的线程池中的线程 12 class MyTh

python 多线程中子线程和主线程相互通信

主线程开启多个线程去干活,每个线程需要完成的时间不同,干完活以后都要通知给主线程,下面代码说明该应用: 代码块: import threading import queue import time import random ''' 需求:主线程开启了多个线程去干活,每个线程需要完成的时间 不同,但是在干完活以后都要通知给主线程 多线程和queue配合使用,实现子线程和主线程相互通信的例子 ''' q = queue.Queue() threads=[] class MyThread(threa

python多线程爬取图片二

上一篇的多线程是使用类创建的,这一次使用函数创建多线程,还是同一个网站https://www.quanjing.com/category/1286521/1.html, 代码如下: 1 # 多线程,自动创建文件夹,每个页面单独存储一个文件夹 2 3 import requests 4 import threading 5 import re 6 import time 7 import queue 8 import os 9 from bs4 import BeautifulSoup 10 11

python多线程之线程锁三(同一时间允许多个线程)

#coding:utf-8 import threading import time num = 0 #全局变量 def runs():     time.sleep(1)     global num #在函数内部要对全局变量进行更改,需要进行声明     samp.acquire() #在操作时,获得锁,4个线程都在里边被锁住     time.sleep(0.001)     num += 1    #虽然4个线程同时对num进行加1,但在相加的时候,CPU还是一个一个的加     pri

Python 多线程Ⅱ

线程模块 Python通过两个标准库thread和threading提供对线程的支持.thread提供了低级别的.原始的线程以及一个简单的锁. threading 模块提供的其他方法: threading.currentThread(): 返回当前的线程变量. threading.enumerate(): 返回一个包含正在运行的线程的list.正在运行指线程启动后.结束前,不包括启动前和终止后的线程. threading.activeCount(): 返回正在运行的线程数量,与len(threa

python基础-------进程线程(二)

Python中的进程线程(二) 一.python中的"锁" 1.GIL锁(全局解释锁) 含义: Python中的线程是操作系统的原生线程,Python虚拟机使用一个全局解释器锁(Global Interpreter Lock)来互斥线程对Python虚拟机的使用.为了支持多线程机制,一个基本的要求就是需要实现不同线程对共享资源访问的互斥,所以引入了GIL.GIL:在一个线程拥有了解释器的访问权之后,其他的所有线程都必须等待它释放解释器的访问权,即使这些线程的下一条指令并不会互相影响.在