1、多个线程执行相同的函数
一个子线程执行完她的函数之后就“死掉了”。
import time
import threading
def say_sorry():
for i in range(5):
print("i am sorry")
time.sleep(1)
t1 = threading.Thread(target= say_sorry)
t2 = threading.Thread(target= say_sorry)
t1.start()
t2.start()
运行结果:
i am sorry
i am sorry
i am sorry
i am sorry
i am sorry
i am sorry
i am sorry
i am sorry
i am sorry
i am sorry
两个一起,两个一起出来
小结:
2、多线程执行的顺序不确定
- 一个程序中,可以有多个线程,执行相同的代码。但是每个线程执行每个线程的功能,互不影响,仅仅是做的事情相同罢了。
- 当在创建Thread对象是target指定的函数代码执行完之后,意味着这个子线程接收。
- 虽然主线程没有了代码,但是它依然会等待所有的子线程结束后,它才会真正的结束,原因是:主线程有个特殊的功能用来对子线程产生的垃圾进行回收。
- 当主线程接收后,才意味着整个程序的真正结束。
import time
import threading
def test1():
for i in range(10):
print("任务1....%d" % i)
time.sleep(0.1)
def test2():
for i in range(5):
print("任务2....%d" % i)
time.sleep(0.2)
t1 = threading.Thread(target= test1)
t2 = threading.Thread(target= test2)
t1.start()
t2.start()
第一次运行结果:
任务1....0
任务2....0
任务1....1
任务2....1
任务1....2
任务1....3
任务2....2
任务1....4
任务1....5
任务2....3
任务1....6
任务1....7
任务2....4
任务1....8
任务1....9
第二次运行结果:
任务1....0
任务2....0
任务1....1
任务1....2
任务2....1
任务1....3
任务2....2
任务1....4
任务1....5
任务2....3
任务1....6
任务1....7
任务2....4
任务1....8
任务1....9
两次结果不一样,说明运行结果出来不同,运行的顺序不确定,除非在t1.start()后面加上时间,才能限制先开始任务1.
总结:多线程执行的顺序不确定,因为在执行代码的时候,当前的运行环境可能不同以及资源的分配可能不同,导致了操作系统在计算接下来应该调用哪个程序的时候得到了不一样的答案,因此顺序不确定。
3、查看线程数量-->查看当前程序运行时的线程
import threading
import time
print(threading.enumerate())
def task_1():
for i in range(5):
print("1111")
time.sleep(1)
t = threading.Thread(target=task_1)
t.start()
print(threading.enumerate())
time.sleep(6)
print(threading.enumerate())
正常运行结果如下:
[<_MainThread(MainThread, started 12345)>]
[<_MainThread(MainThread, started 12345)>, <Thread(Thread-1, started 67890)>]
1111
1111
1111
1111
1111
[<_MainThread(MainThread, started 12345)>, <Thread(Thread-1, started 67890)>]
自己运行结果如下:
[<_MainThread(MainThread, started 2860)>]
1111[<_MainThread(MainThread, started 2860)>, <Thread(Thread-1 (task_1), started 16048)>]
1111
1111
1111
1111
[<_MainThread(MainThread, started 2860)>]
原因:
- 第一行:
[<_MainThread(MainThread, started 2860)>]
- 这是程序开始时打印的主线程列表。
- 第二行:
1111[<_MainThread(MainThread, started 2860)>, <Thread(Thread-1 (task_1), started 16048)>]
- 这行看起来像是两个
print
调用的输出混合在了一起。首先,新线程Thread-1
可能已经开始执行并打印了第一个"1111",然后主线程可能立即(或几乎立即)执行了print(threading.enumerate())
。由于输出缓冲区可能不是原子的(即,两个输出可能同时或几乎同时进入缓冲区),因此它们可能以这种方式混合在一起。 - 注意,这种混合通常不是预期的,但在并发和多线程程序中是可能发生的。
- 这行看起来像是两个
- 接下来的四行:
1111
(共四次)- 这些是新线程
Thread-1
继续执行并打印的剩余"1111"输出。
- 这些是新线程
- 最后一行:
[<_MainThread(MainThread, started 2860)>]
- 这是主线程在暂停6秒后打印的线程列表。此时,新线程
Thread-1
可能已经完成其任务,但由于Python的垃圾回收机制并不保证立即回收已完成的线程对象,因此它可能仍然出现在列表中(尽管这取决于Python解释器的具体实现)。然而,更常见的是,如果线程确实完成了任务并且没有其他引用指向它,那么它可能已经从列表中消失。
- 这是主线程在暂停6秒后打印的线程列表。此时,新线程
重要说明:
- 在实际的多线程程序中,输出可能会以不同的顺序出现,因为线程的调度是由操作系统和Python的线程实现(特别是GIL的释放和获取)控制的。
- 输出的混合(如第二行所示)通常不是可预测的,并且可能不会在每次运行时都发生。
- 线程的结束和垃圾回收的确切时机也会影响最后打印的线程列表的内容。
小结:
- 通过第4行的打印能够说明,当程序运行之后默认有1个线程。
- 当第13行创建一个Thread对象,第14 行运行这个对象,且在第16行打印的时候有2个线程,那么就说明Thread真的是创建了一个线程。
- 当一个线程结束之后,在第19行打印时只有1个,那么就表示刚刚那个子线程已经结束了。
- 通过第14行、17行的打印不同说明了,创建一个Thread对象根本不会创建一个线程,仅仅是一个对象罢了,而当调用了它的start方法之后,才会真正创建一个新的子线程。
- 什么时候调用start什么时候才会真正的创建新线程。
- enumerate()--列表