python之路-day33-线程相关1

 

1、线程的理论python

  一、什么是线程多线程

   1) 在传统操做系统中,每一个进程有一个地址空间,并且默认就有一个控制线程ide

    2)线程顾名思义,就是一条流水线工做的过程(流水线的工作须要电源,电源就至关于cpu),而一条流水线必须属于一个车间,ui

  一个车间的工做过程就是一个进程,车间负责把资源整合到一块儿,是一个资源单位,而一个车间至少有一条流水线。 spa

    3)因此:进程只是用来把资源集中到一块儿(进程只是一个资源单位,或者时候资源集合),而线程才是cpu上的执行单位操作系统

    4) 多线程(即多个控制线程)的概念是,在一个进程中存在多个线程,多个线程共享该进程的地址空间,至关与一个车间内有线程

  多天流水线,都公用一个车间的资源。例如,北京地铁与上海地铁是不一样的进程,而北京地铁里的13号线是一个线程,北京地铁全部设计

  的线路共享北京地铁全部的资源,好比全部的乘客能够被全部线路拉。3d

 

  二、线程与进程的区别code

  1)同一个进程内的多个线程共享该进程内的地址资源

  2)建立线程的开销要远小于建立进程的开销(建立一个进程,就是建立一个车间,设计到申请空间,并且在该空间

  内至少建一条流水线,但建立线程,就只是在一个车间内造一条流水线,无需申请空间,因此建立开销小)

 

2、开启线程的两种方式

  一、threading模块介绍

  multiprocess模块的彻底模仿了threading模块的接口,两者在使用层面,有很大的类似性,由于不在详细的介绍

 

  二、开启线程的两种方式

 1 # 开启线程的两种方式
 2 
 3 # 方式一
 4 # from threading import Thread
 5 # import time
 6 #
 7 # def sayhi(name):
 8 #     time.sleep(2)
 9 #     print('%s say hello'%name)
10 #
11 # if __name__ == '__main__':
12 #     t = Thread(target=sayhi,args=('egon',))
13 #     t.start()
14 #     print('主线程')
15 
16 # 方式二
17 import time
18 from threading import Thread
19 
20 class Sayhi(Thread):
21     def __init__(self,name):
22         super().__init__()
23         self.name = name
24 
25     def run(self):
26         time.sleep(2)
27         print('%s say hello'%self.name)
28 
29 if __name__ == '__main__':
30     t = Sayhi('egon')
31     t.start()
32     print('主线程')
建立线程的两种方式

 

3、多线程和多进程的区别

  一、谁的开启速度快

from threading import Thread

def work():
    print('hello')

if __name__ == '__main__':
    t = Thread(target=work)
    t.start()
    print('主线程/主进程')

执行结果以下,几乎t.start()的同时就将线程开启了,而后打印出了hello,证实  线程的建立开销极小

hello
主线程/主进程

  

from multiprocessing import Process

def work():
    print('hello')

if __name__ == '__main__':
    #在主进程下开启子进程
    p = Process(target=work)
    p.start()
    print('主进程/主线程')

# 执行结果以下,p.start()将开启进程的信号发给操做系统,操做系统要申请内存
# 空间,拷贝父进程地址空间到子进程,开销远大于线程
# 主进程/主线程
# hello

  

  二、瞅一瞅 pid

 1 # 在主进程下开启多个线程,每一个线程都跟主进程的pid同样
 2 # from threading import Thread
 3 # import os
 4 #
 5 # def work():
 6 #     print('hello',os.getpid())
 7 #
 8 # if __name__ == '__main__':
 9 #     t1 = Thread(target=work)
10 #     t2 = Thread(target=work)
11 #     t1.start()
12 #     t2.start()
13 #     print('主线程/主进程pid',os.getpid())
14 #
15 # 结果:
16 # hello 9856
17 # hello 9856
18 # 主线程/主进程pid 9856
19 
20 # 开多个进程,每一个进程都有不一样的pid
21 # from multiprocessing import Process
22 # import os
23 # 
24 # def work():
25 #     print('hello',os.getpid())
26 # 
27 # if __name__ == '__main__':
28 #     p1 = Process(target=work)
29 #     p2 = Process(target=work)
30 #     p1.start()
31 #     p2.start()
32 #     print('主进程/主线程',os.getpid())
33 # 
34 # 结果:
35 # 主进程/主线程 38384
36 # hello 37008
37 # hello 36496
线程、进程中的pid

 

  三、同一进程内的线程共享该进程的数据?

 1 # 进程之间地址空间是隔离的
 2 # from multiprocessing import Process
 3 # import os
 4 #
 5 # def work():
 6 #     global n
 7 #     n = 0
 8 #
 9 # if __name__ == '__main__':
10 #     n = 100
11 #     p = Process(target=work)
12 #     p.start()
13 #     p.join()
14 #     print('主',n)
15 #
16 # 结果分析:毫无疑问子进程p已将本身的全局n改为了0.可是改的仅仅是本身的
17 # 父进程的n仍然为100
18 
19 
20 #  同一进程内开启的多个线程是共享该进程地址空间的
21 # from threading import Thread
22 # import os
23 # 
24 # def work():
25 #     global n
26 #     n = 0
27 # if __name__ == '__main__':
28 #     n = 100
29 #     t = Thread(target=work)
30 #     t.start()
31 #     t.join()
32 #     print('主',n)
33 # 结果:查看结果为0,由于统一进程内的线程之间共享进程内的数据
34 # 主 0
进程与进程\进程与线程之间数据问题

 

 

4、Thread 对象的其余属性或方法

  介绍:

# isAlive():    返回线程是否活动的。
# getName():    返回线程名。
# setName():    设置线程名

threading 模块提供的一些方法:

# threading.currentThread():    返回当前线程变量
# threading.enumerate():    返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程
# threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果

  验证:

 1 # from threading import Thread
 2 # import threading
 3 # from multiprocessing import Process
 4 # import os,time
 5 #
 6 # def work():
 7 #     time.sleep(3)
 8 #     print(threading.current_thread().getName())
 9 # if __name__ == '__main__':
10 #     # 在主进程下开启线程
11 #     t = Thread(target=work)
12 #     t.start()
13 #     print(threading.current_thread().getName())
14 #     print(threading.current_thread())
15 #     print(threading.enumerate())
16 #     print(threading.activeCount)
17 #     print('主进程\主线程')
18 #
19 # 结果:
20 # MainThread
21 # <_MainThread(MainThread, started 39404)>
22 # [<_MainThread(MainThread, started 39404)>, <Thread(Thread-1, started 39468)>]
23 # <function active_count at 0x000001CBF2F45BF8>
24 # 主进程\主线程
25 # Thread-1
Thread对象的其余属性方法
 1 # 主线程等待子进程结束
 2 # from threading import Thread
 3 # import time
 4 # def sayhi(name):
 5 #     time.sleep(2)
 6 #     print('%s say hello'%name)
 7 # if __name__ == '__main__':
 8 #     t = Thread(target=sayhi,args=('egon',))
 9 #     t.start()
10 #     t.join()
11 #     print('主进程')
12 #     print(t.is_alive())
13 # 
14 # 执行结果:
15 # egon say hello
16 # 主进程
17 # False
主线程等待子线程结束

 

 

5、守护线程

  不管是进程仍是线程,都遵循:守护xxx会主动等待主xxx运行完毕后被销毁

  须要强调的是:运行完毕并不是终止运行

  

一、对主进程来讲,运行完毕指的是主进程代码运行完毕

二、对主线程来讲,运行完毕指的是主线程所在的进程内全部非守护线程通通运行完毕,主线程才算运行完毕


详细解释:
一、主进程在其代码结束后就已经运算完毕了(守护进程在此时就被回收),而后主进程
会一直等飞受贿的子进程都运行完毕后回收子进程的资源(不然会产生僵尸进程),才结束

二、主线程在其余费守护线程运行完毕后才算运行完毕(守护线程在刺死就被回收)。由于主线程的结束意味着进程的结束,基础讷航总体的资源都将被回收,而基础讷航必须保证非守护线程都运行完毕后才能结束。

  验证:

# from threading import Thread
# import time
# def sayhi(name):
#     time.sleep(2)
#     print('%s say hello'%name)
# 
# if __name__ == '__main__':
#     t = Thread(target=sayhi,args=('egon'))
#     t.setDaemon(True) # 必须在t.start()以前设置
#     t.start()
#     print('主线程')
#     print(t.is_alive())
# 
# 执行结果:
# 主线程
# True
守护线程验证

 

 

6、死锁现象与递归锁

  一、所谓死锁:是指两个或两个以上的进程或线程执行过程当中,由于争夺资源而形成的一种互相等待的现象,若无外力

  做用,它们都将没法推动下去。此时称系统处于死锁或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,以下就是死锁

 1 # from threading import Thread,Lock
 2 # import time
 3 # 
 4 # muteA = Lock()
 5 # muteB = Lock()
 6 # 
 7 # class MyThread(Thread):
 8 #     def run(self):
 9 #         self.fun1()
10 #         self.fun2()
11 # 
12 #     def fun1(self):
13 #         muteA.acquire()
14 #         print('%s拿到A锁'%self.name)
15 # 
16 #         muteB.acquire()
17 #         print('%s 拿到B锁'%self.name)
18 #         muteB.release()
19 #         muteA.release()
20 # 
21 #     def fun2(self):
22 #         muteB.acquire()
23 #         print("%s 拿到B锁"%self.name)
24 #         time.sleep(2)
25 # 
26 #         muteA.acquire()
27 #         print("%s 拿到A锁"%self.name)
28 #         muteA.release()
29 # 
30 #         muteB.release()
31 # 
32 # if __name__ == '__main__':
33 #     for i in range(10):
34 #         t = MyThread()
35 #         t.start()
36 #         
37 # 结果:
38 # Thread-1拿到A锁
39 # Thread-1 拿到B锁
40 # Thread-1 拿到B锁
41 # Thread-2拿到A锁
死锁

  二、递归锁

  解决办法:递归锁,在python中为了支持在同一线程中屡次请求同一资源,python提供了可重入锁RLock个RLock内部维护着

一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源能够被屡次require。直到一个线程全部的acquire都

被release,其余的线程才能得到资源。上面的例子若是使用RLock代替Lock,则不会发生死锁,两者的区别是:递归锁能够连续

acquire屡次,而互斥锁只能acquire一次

 1 from threading import Thread,RLock
 2 import time
 3 
 4 mutexA=mutexB=RLock() #一个线程拿到锁,counter加1,该线程内又碰到加锁的状况,则counter继续加1,这期间全部其余线程都只能等待,等待该线程释放全部锁,即counter递减到0为止
 5 
 6 class MyThread(Thread):
 7     def run(self):
 8         self.func1()
 9         self.func2()
10     def func1(self):
11         mutexA.acquire()
12         print('\033[41m%s 拿到A锁\033[0m' %self.name)
13 
14         mutexB.acquire()
15         print('\033[42m%s 拿到B锁\033[0m' %self.name)
16         mutexB.release()
17 
18         mutexA.release()
19 
20     def func2(self):
21         mutexB.acquire()
22         print('\033[43m%s 拿到B锁\033[0m' %self.name)
23         time.sleep(2)
24 
25         mutexA.acquire()
26         print('\033[44m%s 拿到A锁\033[0m' %self.name)
27         mutexA.release()
28 
29         mutexB.release()
30 
31 if __name__ == '__main__':
32     for i in range(10):
33         t=MyThread()
34         t.start()
递归锁
相关文章
相关标签/搜索