原文:https://www.cnblogs.com/i-honey/p/7823587.html

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

Thread类

#类签名

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

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

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

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

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

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

线程启动:

1
2
3
4
5
6
7
8
9
10
11
12
13
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

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

线程的传参:

1
2
3
4
5
6
7
8
9
10
11
12
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、线程内的函数抛出未处理的异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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)>
...

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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整数

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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。

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1def start(self):
    _start_new_thread(self._bootstrap, ())
    ....
 
2、_start_new_thread = _thread.start_new_thread
 
3def start_new_thread(function, args, kwargs=None):
    pass
 
4def _bootstrap(self):
    self._bootstrap_inner()
 
5def _bootstrap_inner(self):
    ....
    try:
        self.run()#最终start()方法调用了run()方法
    except SystemExit:
        pass

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

run()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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
2
3
4
5
6
7
8
9
10
11
1def __init__(self, group=None, target=None, name=None,
                 args=(), kwargs=None, *, daemon=None):
    self._target = target
    self._args = args
    self._kwargs = kwargs
    ....
 
2def 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()方法

Python 中的线程-进程2的更多相关文章

  1. Python 中的线程-进程1

    原文:https://www.cnblogs.com/i-honey/p/8042047.html 1. 并行和并发 并行:同时做某些事,可以互不干扰的同一时刻做几件事. 并发:也是同时做某些事,但是 ...

  2. Python中的线程和进程

    引入进程和线程的概念及区别 threading模块提供的类:   Thread, Lock, Rlock, Condition, [Bounded]Semaphore, Event, Timer, l ...

  3. python中的线程技术

    #!/user/bin/env python # @Time :2018/7/7 11:42 # @Author :PGIDYSQ #@File :DaemonTest.py import threa ...

  4. 理解 Python 中的线程

    原地址:http://blog.jobbole.com/52060/ 本文由 伯乐在线 - acmerfight 翻译自 Akshar Raaj.欢迎加入技术翻译小组.转载请参见文章末尾处的要求. 我 ...

  5. 【Python】解析Python中的线程与进程

    基础知识 线程 进程 两者的区别 线程的类型 Python 多线程 GIL 创建多线程 线程合并 线程同步与互斥锁 可重入锁(递归锁) 守护线程 定时器 Python 多进程 创建多进程 多进程通信 ...

  6. Python之路-Python中的线程与进程

    一.发展背景 任务调度 大部分操作系统(如Windows.Linux)的任务调度是采用时间片轮转的抢占式调度方式,也就是说一个任务执行一小段时间后强制暂停去执行下一个任务,每个任务轮流执行.任务执行的 ...

  7. 15.python并发编程(线程--进程--协程)

    一.进程:1.定义:进程最小的资源单位,本质就是一个程序在一个数据集上的一次动态执行(运行)的过程2.组成:进程一般由程序,数据集,进程控制三部分组成:(1)程序:用来描述进程要完成哪些功能以及如何完 ...

  8. Python学习之==>线程&&进程

    一.什么是线程(thread) 线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一个线程指的是进程中一个单一顺序的控制流,一个进程中可以包含多个线程,每条线程并行 ...

  9. python中的线程锁

    锁对象 原始锁是一个在锁定时不属于特定线程的同步基元组件.在Python中,它是能用的最低级的同步基元组件,由 _thread 扩展模块直接实现. 原始锁处于 "锁定" 或者 &q ...

随机推荐

  1. Spark Streaming中的操作函数讲解

    Spark Streaming中的操作函数讲解 根据根据Spark官方文档中的描述,在Spark Streaming应用中,一个DStream对象可以调用多种操作,主要分为以下几类 Transform ...

  2. mysql 授权的时候库名不能添加单引号homestead.* 写成 '库名'.* 错的语法

    create user 'wechat'@'192.168.10.%' identified by 'xxxxx'; create database 库名DEFAULT CHARSET utf8 CO ...

  3. mssql占用80端口解决办法

    services.msc

  4. Geometric deep learning on graphs and manifolds using mixture model CNNs

    Monti, Federico, et al. "Geometric deep learning on graphs and manifolds using mixture model CN ...

  5. C++函数的高级特性

    对比于 C 语言的函数,C++增加了重载(overloaded).内联(inline).const 和 virtual 四种新机制.其中重载和内联机制既可用于全局函数也可用于类的成员函数,const ...

  6. (转)如何基于FFMPEG和SDL写一个少于1000行代码的视频播放器

    原文地址:http://www.dranger.com/ffmpeg/ FFMPEG是一个很好的库,可以用来创建视频应用或者生成特定的工具.FFMPEG几乎为你把所有的繁重工作都做了,比如解码.编码. ...

  7. R read.table函数的check.names参数

    今天用cummeRbund 对cuffdiff的结果进行可视化, 一直报错,之前跑的好好的,找了半天原因, 原来出现在read.table这个函数上: read.table有一个参数check.nam ...

  8. vncserver的安装和使用

    环境:RedHat Linux 6企业版.Xwindows:gnome (红帽默认安装的图形界面) 尽管我们可以使用SSH连接远程通过字符界面来操作Linux,但是对于更多熟悉图形人来说是很不方便的, ...

  9. J2SE基础:8.系统经常使用类二

    1:基础数据与封装类型之间的转型 A:基础数据类型--->封装类型(对象类型) Boolean boolean_1 = new Boolean(true); byte ---->Byte ...

  10. 移动端 touch

    原文链接:http://caibaojian.com/mobile-touch-event.html 本文主要介绍 TouchEvent 相关的一些对象与属性如 Touch, TouchList, t ...