Python线程详解

Python线程详解

    # 进程的三状态:就绪 运行 阻塞
    # multiprocessing模块
        # Process-开启进程
        # Lock - 互斥锁
            # 为什么要在进程中加锁
                # 因为进程操作文件也会发生数据不安全
        # Queue -队列 IPC机制(Pipe,redis,memcache,rabbitmq,kafka)
            # 生产者消费者模型
        # Manager - 提供数据共享机制

------------------Process-开启进程----------
可查看链接https://www.cnblogs.com/Marcki/p/10111927.html
---------线程学习--------
1、启动线程start
线程的异步

2、开启多个子线程
3、join方法
4、测试
进程和线程的开启效率差
数据隔离还是共享
5、守护线程


1)启动线程start

(1从线程导入线程 2实例化Thread,传递线程函数。3对象.start())

```
import os
from threading import Thread
#multiprocessing完全是仿照threading类写的
def func():
print(os.getpid())
#启动线程 start
Thread(target=func).start()
print('---->',os.getpid())
-------------结果:
5804
----> 5804

```
#由上可知,1、主线程和子线程在同一个进程里,pid一致。2、在Windows里面不需要写if name=='main'了。3、先打印子线程里的内容,再打印主线程里内容,与进程相反,说明线程开启快,快到主线程还没执行下一句代码这个子线程就创建并执行了。

2)线程是异步的,并发的

```

import os,time
from threading import Thread
def func():
print('start son thread')
time.sleep(1)
print('end son thread')
Thread(target=func).start()
print('start',os.getpid())
time.sleep(0.5)
print('end',os.getpid())
--------------结果:
start son thread
start 5280
end 5280
end son thread

```

3)开启多个子线程

import os,time
from threading import Thread
def func():
print('start son thread')
time.sleep(1)
print('end son thread',os.getpid())
for i in range(3):
Thread(target=func).start()
----------------结果:
start son thread
start son thread
start son thread
end son thread 6772
end son thread 6772
end son thread 6772

4)往线程函数里传参,通过args,和进程一样。线程的调度仍然是操作系统决定的,谁的时间片到了就运行谁的

```

import os,time
from threading import Thread
def func(arg):
print('start son thread')
time.sleep(1)
print('end son thread',os.getpid(),arg)
for i in range(3):
Thread(target=func,args=(i,)).start()
------------结果:
start son thread
start son thread
start son thread
end son thread 7112 0
end son thread 7112 1
end son thread 7112 2

```

5)1、主线程等待所有子线程结束之后才结束。2、主线程如果结束了,主进程也就结束了。

import os,time
from threading import Thread
def func(arg):
print('start son thread')
time.sleep(1)
print('end son thread',os.getpid(),arg)
for i in range(3):
Thread(target=func,args=(i,)).start()
print("main")
--------------结果:
start son thread
start son thread
start son thread
main #主线程代码执行完了,等待子线程结束
end son thread 5416 0
end son thread 5416 1
end son thread 5416 2
Process finished with exit code 0 #所有子线程结束,主线程才结束,然后主进程结束
5)[1]join方法 阻塞 直到子线程执行结束
#用join,那么线程对象和start就不要放在一起了,单执行对象.start()来开启
import os,time
from threading import Thread
def func(arg):
print('start son thread')
time.sleep(1)
print('end son thread',os.getpid(),arg)
t=Thread(target=func,args=(0,))
t.start()
t.join()
print("子线程执行结束!")
----------------------结果:
start son thread
end son thread 6756 0
子线程执行结束!
[2]创建多个子线程,t.join()在循环外,这时t代表for循环最后一个值3,所有只是阻塞最后一个创建的t对象。所以每次打印“子线程执行结束!”都是在“end son thread 4864 3”之后。但是这时执行慢的子线程就没被阻塞就开始执行主线程中代码了。
import os,time
from threading import Thread
def func(arg):
print('start son thread')
time.sleep(1)
print('end son thread',os.getpid(),arg)
for i in range(4):
t=Thread(target=func,args=(i,))
t.start()
t.join()
print("子线程执行结束!")
--------------结果:
start son thread
start son thread
start son thread
start son thread
end son thread 4864 0
end son thread 4864 2
end son thread 4864 3
子线程执行结束!
end son thread 4864 1
[3]保证所有子线程都结束之后再继续执行主线程中的代码
#注:将所有线程对象都追加到列表,循环列表,将每个对象.join阻塞。所有线程对象执行结束然后才执行主线程中程序
import os,time
from threading import Thread
def func(arg):
print('start son thread')
time.sleep(1)
print('end son thread',os.getpid(),arg)
li=[]
for i in range(4):
t=Thread(target=func,args=(i,))
t.start()
li.append(t)
for t in li:t.join()
print("子线程执行结束!")
--------------结果:
start son thread
start son thread
start son thread
start son thread
end son thread 6480 0
end son thread 6480 1
end son thread 6480 2
end son thread 6480 3
子线程执行结束!

6)[1]使用面向对象的方式启动线程

#注意:用自己类创建的对象记得start开启线程
#1、继承Thread类2、定义run方法,run方法是线程执行函数
import os,time
from threading import Thread
class MyThread(Thread):
def run(self):
print('start son thread')
time.sleep(1)
print('end son thread',os.getpid())
for i in range(3):
MyThread().start()
------------------结果:
start son thread
start son thread
start son thread
end son thread 6116
end son thread 6116
end son thread 6116
[2]面向对象启动线程并往线程函数里传参
#1、创建init方法,传参进入类中 2、执行父类的init方法,不执行报错入下
import os,time
from threading import Thread
class MyThread(Thread):
def init(self,name):
super().init() #执行父类init方法在实例变量后面报错了,不知道怎么回事,有时间测试一下
self.name=name

def run(self):
    time.sleep(1)
    print('end son thread',os.getpid(),self.name)

for i in range(3):
MyThread("mcw").start()
------------------结果:
end son thread 2060 mcw
end son thread 2060 mcw
end son thread 2060 mcw

不执行父类init报错:
assert self._initialized, "Thread.init() not called"
AssertionError: Thread.init() not called

[3]start执行的原理
class Foo:
def start(self):
self.run()
def run(self):
print('run in Foo')
class Son(Foo):
def run(self):
print('run in son')
Son().start()
---------------结果:
run in son
[4]自定义类中把init方法去掉后,实例化的时候还带有传参报错
t=MyThread("mcw")
File "C:\python3\lib\threading.py", line 780, in init
assert group is None, "group argument must be None for now"
AssertionError: group argument must be None for now

7)线程里面的其它方法 查看线程id

[1]用类来启动线程,类里面和外面查看线程的id。对象.ident self.ident。注意:在init里定义与父类相同的实例变量名,会被覆盖掉的。
import os,time
from threading import Thread
class MyThread(Thread):
def run(self):
time.sleep(1)
print(' thread id',self.ident)
for i in range(3):
t=MyThread()
t.start()
print("线程id",t.ident)
[2]current_thread()在哪个线程里代表哪个线程。用函数来启动线程,查看线程id 。
#当前线程current_thread()对象,记得加括号(),函数中调用对象
import time
from threading import current_thread,Thread
def func(i):
print('start son thread',i,current_thread(),type(current_thread()))
print("线程函数中调用执行这个函数的线程对象的线程id:%s"%current_thread().ident)
time.sleep(1)
print('end son thread',)
t=Thread(target=func,args=(1,))
t.start()
print("main",t,t.ident)
--------------结果:
start son thread 1 <Thread(Thread-1, started 4444)> <class 'threading.Thread'>
线程函数中调用执行这个函数的线程对象的线程id:4444
main <Thread(Thread-1, started 4444)> 4444
end son thread

from threading import current_thread,Thread
def func():
print('end son thread',current_thread().ident)
t=Thread(target=func).start()
print("main thread",current_thread().ident)
------------结果:
end son thread 1936 #current_thread()在哪个线程里代表哪个线程
main thread 4928
[3]enumerate active_count
from threading import current_thread,Thread,active_count,enumerate
def func():
print('end son thread',current_thread().ident)
t=Thread(target=func).start()
print(enumerate(),enumerate) #主线程和子线程两个。enumerate()表示当前运行的线程,
print(active_count(),active_count) #active_count()代表当前运行的线程的个数,相当于len(enumerate())
-------------------结果:
end son thread 4324
2
[<_MainThread(MainThread, started 2460)>, <Thread(Thread-1, started 4324)>] <function enumerate at 0x006E1270>
2 <function active_count at 0x006E11E0>
[4]terminate 能结束进程 。
在线程中不能从主线程结束一个子线程

8)进程和线程的开启效率差。进程一般开cpu个数的1到2倍,开多了浪费系统资源

import time
from threading import Thread
from multiprocessing import Process
def func(a,b):
c=a+b
if name=="main":
p_start=time.time()
p_li=[]
for i in range(100):
p=Process(target=func,args=(i,i2))
p.start()
p_li.append(p)
for p in p_li:p.join()
print("进程:",time.time()-p_start)
t_start=time.time()
t_li = []
for i in range(100):
t=Thread(target=func,args=(i,i
2))
t.start()
t_li.append(t)
for t in t_li:t.join()
print("线程:",time.time() - t_start)
----------------结果:
进程: 6.1583521366119385
线程: 0.01500082015991211 #几百倍速度差距

9)线程共享进程中的数据。子线程在进程中共享数据,

from threading import Thread
n=100
def func():
global n #最好不要轻易修改全局变量
n-=1
t_li=[]
for i in range(100):
t=Thread(target=func)
t_li.append(t)
t.start()
for t in t_li:t.join()
print(n)
-----------结果:
0

10)守护线程

[1]没有守护线程的时候
import time
from threading import Thread
def son1():
while True:
time.sleep(0.5)
print("in son1")
t=Thread(target=son1)
t.start()
---------结果:
一直打印“in son1”
[2]有守护线程的时候,线程开启后,后面没有运行一段时间的代码
import time
from threading import Thread
def son1():
while True:
time.sleep(0.5)
print("in son1")
t=Thread(target=son1)
t.daemon=True
t.start()
---------------结果:
什么都没有打印
#这说明后面没代码了,主线程结束守护线程就结束了
[3]有守护线程的时候,线程开启后,后面有运行一段时间的代码time.sleep(3)
import time
from threading import Thread
def son1():
while True:
time.sleep(0.5)
print("in son1")
t=Thread(target=son1)
t.daemon=True
t.start()
time.sleep(3)
----------结果:
打印5次"in son1"后结束
#这说明后面还有代码,主线程执行完代码结束后守护线程就结束了
[4]
import time
from threading import Thread
def son1():
while True:
time.sleep(0.5)
print("in son1")
t=Thread(target=son1)
t.daemon=True
t.start()
time.sleep(3.1)
---------结果:
打印6次"in son1"后结束
#和上面打印5次对比,5次是因为主线程执行的3秒内,守护线程一直在运行,守护线程0.5秒打印一次,3秒到了主线程结束守护线程3秒也就结束。守护线程每0.5秒后打印一次,3秒后因为主线程停止守护线程也停止所以没有打印第6次
#这里将主线程运行时间+0.1,也就是主线线程运行时间大于3秒,那么守护线程也运行3秒以上,刚过3秒就运行到打印“in son1”,所以打印6次
[5]
import time
from threading import Thread
def son1():
while True:
time.sleep(0.5)
print("in son1")
def son2():
for i in range(5):
time.sleep(1)
print("in son2")
t=Thread(target=son1)
t.daemon=True
t.start()
Thread(target=son2).start()
time.sleep(3)
-----------结果:
in son1
in son2
in son1
.......
"in son2"打印5次,"in son1"打印9次
#这说明守护线程一直等到所有的线程都结束之后才结束的
#这说明守护线程除了守护主线程的代码之外也会守护子线程
#主线程代码执行完毕,主线程没有关闭。主线程等待所有子线程结束之后才结束。守护线程守护主线程,所以守护线程也要等所有子线程结束之后才结束。如果子线程都结束了,主线程还有程序在运行,那么守护线程还是在守护主线程。
[6]
import time
from threading import Thread
def son1():
while True:
time.sleep(0.5)
print("in son1")
t=Thread(target=son1)
t.daemon=True
t.start()
time.sleep(3)
print('1')
print('1')
print('1')
----------结果:
六次 “in son1”
1
1
1
#原因:为什么主线程有代码在运行的时候,守护线程没有做打印操作了呢,因为这里守护线程是0.5s打印一次,主线程打印3次“1”所花费的时间不足0.5s不够守护线程到达下一次打印的时间。为什么打印6次呢,因为主线程sleep3秒正好达到守护线程打印第6次的时间,在主线程第3秒到打印出第一个“1”这个时间之间,守护线程才打印出第6个“in son1”。time.sleep(3)后面没有代码,守护线程在第3秒是没有执行到打印就随着主线程停止而停止了的。
[7]
import time
from threading import Thread
def son1():
while True:
time.sleep(0.5)
print("in son1")
t=Thread(target=son1)
t.daemon=True
t.start()
time.sleep(3)
print('1')
time.sleep(1)
print('1')
---------------结果:
六个“in son1” 然后:
1
in son1
in son1
1

11)在多进程里启动多线程

import os
from multiprocessing import Process
from threading import Thread

def tfunc():
print('tfunc线程',os.getpid())
def pfunc():
print('pfunc进程-->',os.getpid())
Thread(target=tfunc).start()

if name == 'main':
Process(target=pfunc).start()
------------------结果:
pfunc进程--> 9716
tfunc线程 9716

方法总结:
开启进程执行进程函数
进程函数里开启线程执行线程函数
线程函数里执行线程运行的程序

posted @ 2019-05-22 19:24  马昌伟  阅读(304)  评论(0编辑  收藏  举报