python学习笔记(32)多线程&多进程

一、多线程&多进程

  对于操作系统来说,一个任务就是一个进程。比如我在电脑上打开视频看电视,再启动QQ,这样打开视频和启动QQ就是两个进程了 。进程是多个资源的集合

  每个进程中可以做很多事情,比如我打开QQ,可以与A打字聊天,同时还可以与B视频,接收C的文件,一个进程中可以有很多线程来干活,这样一个QQ需要运行多个子任务,我们把这些子任务叫做 线程(thread)

  每个进程中至少有一个线程在干活,比如我打开QQ,即使不做任何操作,还是保留了一个QQ窗口,这就是主线程,子线程会等着主线程来调

  我们在做事情的时候,一个人做是比较慢的,如果多个人一起来做的话,就比较快了,程序也是一样的,我们想运行的速度快一点的话,就得使用多进程,或者多线程,在python里面,多线程被很多人诟病,为什么呢,因为Python的解释器使用了GIL的一个叫全局解释器锁,它不能利用多核CPU,

  只能运行在一个cpu上面,但是你在运行程序的时候,看起来好像还是在一起运行的,是因为操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换到任务2,任务2执行0.01秒,再切换到任务3,执行0.01秒……这样反复执行下去。

  表面上看,每个任务都是交替执行的,但是,由于CPU的执行速度实在是太快了,我们感觉就像所有任务都在同时执行一样。这个叫做上下文切换。  

·  当需要使用大量网络IO、磁盘IO,不停的读写磁盘等(如存数据库)操作时,最好使用多线程来操作

   当需要使用大量消耗CPU性能时,比如排序,复杂计算等,最好使用多进程,因为多进程才能同时使用更多的cpu来进行处理数据

  多线程,线程之间的数据是共享的

  多进程,每个进程之间的数据是独立的

  

二、python中通过threading模块来实现多线程

  简单启动线程的例子

import threading
    import time
    def sayhi(num): #定义每个线程要运行的函数
     
        print("running on number:%s" %num)
     
        time.sleep(3)


case_result = []
     def run_case(casename):
          case_result.append({casename:'success'})
     
#多线程运行函数时,函数的返回结果拿不到,需要定义一个list将结果append进去

    if __name__ == '__main__':
        t1 = threading.Thread(target=sayhi,args=(1,)) #生成一个线程实例
        t2 = threading.Thread(target=sayhi,args=(2,)) #生成另一个线程实例
        t1.start() #启动线程
        t2.start() #启动另一个线程

  

  

  

主线程,也就是程序一开始运行的时候,最初的那个线程
子线程,通过thread类实例化的线程,都是子线程
如果加了线程等待,主线程等待子线程,执行结束后,主线程再去做别的操作。否则主线程在启动子线程后,不会等待子线程结束,而是继续走主线程代码

下面举例说明两种线程等待的方式
import threading
import time
def insert_db():
    time.sleep(3)
    print('insert_db over')

#串形的方式,非多线程
# start_time = time.time()
# for i in range(3):
#     insert_db()
# end_time = time.time()
# print('串行的执行的时间',end_time - start_time )

threads = []
start_time2 = time.time()

#1、第一种方式,麻烦一点
# for i in range(3):
#     t = threading.Thread(target=insert_db)#有入参时,需要加args=()或者args=[]来传递参数,当只有一个入参时元组记得加逗号,args=(1,)
#     t.start()
#     threads.append(t)
#
# for t in threads:
#     t.join()#使用join实现主线程等待子线程结束

#2、第二种方式,思路是判断当前存活的线程个数
for i in range(3):
    t = threading.Thread(target=insert_db)
    t.start()
#当线程数为1,即子线程结束,代码才往下走,否则在死循环中
while threading.activeCount()!=1:#活着的线程数
    pass

end_time2 = time.time()
print('多线程执行的时间',end_time2 - start_time2)

  

例子:通过多线程实现下载QQ头像

 1 import time
 2 
 3 import requests,hashlib,threading
 4 
 5 def down_load_pics(url):
 6     req = requests.get(url)
 7     title = hashlib.md5(url.encode())
 8     with open(title.hexdigest()+'.png','wb') as fw:
 9         fw.write(req.content)
10 
11 url_list = ['https://q4.qlogo.cn/g?b=qq&nk=1345741814&s=140',
12         'https://q4.qlogo.cn/g?b=qq&nk=1134900814&s=140',
13         'https://q4.qlogo.cn/g?b=qq&nk=511402865&s=140'
14        ]
15 
16 start_time = time.time()
17 for url in url_list:
18     t = threading.Thread(target=down_load_pics,args=(url,))
19     t.start()
20 
21 while threading.activeCount()!=1:#等待子线程结束
22     pass
23 
24 end_time = time.time()
25 print(end_time - start_time)
守护线程:当主线程结束后,守护线程会自动结束。就比如秦始皇死后,所有为他建造皇陵的人全部都要去陪葬。那么秦始皇就是主线程,其他陪葬的都是子线程。

以一个视频聊天为例,关闭QQ后,所有的子线程会停止
#守护线程,一但主线程死掉,那么守护线程不管有没有执行完成,全都结束
import threading
import time
def talk(name):
    print('正在和%s聊天'%name)
    time.sleep(200)

def shipin(name):
    print('正在和%s视频'%name)
    time.sleep(300)

print("qq主窗口")
t1 = threading.Thread(target=talk,args=['刘小燕'])
t1.setDaemon(True) #设置线程为守护线程
t1.start()

t2 = threading.Thread(target=shipin,args=['蔡明昌'])
t2.setDaemon(True) #设置线程为守护线程
t2.start()

time.sleep(5)

print('结束。。。')

  

线程锁: 

  经常出现在数据库操作时,如果多线程同时操作一个数据库操作时,由于线程有快慢先后的区别,原来的数据处理完可能会出错。那么在一个线程来的时候,加上一把锁,只有等这个线程干活结束后,再把锁打开,下一个线程再去操作数据干活,这样数据就不会乱套了

  

import threading
count = 0
lock = threading.Lock()#申请一把锁
def test():
    global count
    print(threading.current_thread())
    '''第一个加锁方式,不解锁后出现线程死锁'''
    # lock.acquire()#加锁
    # count+=1
    # lock.release()#解锁
    '''第二种加锁方式,会自动解锁'''
    with lock:
        count+=1

for i in  range(3):
    t = threading.Thread(target=test)
    t.start()

  

  

三、进程通过multiprocessing模块实现

  进程和线程用法和线程相似,都有等待、锁、等操作,一个进程内部可以再启动多个线程

  进程原来是使用多个CPU来处理,每个进程之间是独立的,处理的数据是相互独立,而线程处理的数据是共享的,所以多进程的锁意义不大。

  

import multiprocessing
import time
import threading

lock = multiprocessing.Lock()
a =1

def xxx():
    pass
def mytest():
    '''进程中再启20个线程调用xxx方法'''
    for i in range(20):
        t = threading.Thread(target=xxx)
        t.start()

    time.sleep(3)
    global a
    with lock:#加锁并自动解锁
        a+=1
    print('over')

# process = []
#启动进程需要放在main中
if __name__ == '__main__':
    for i in range(5):
        p = multiprocessing.Process(target=mytest,name='tyl')
        p.start()
        # process.append(p)
    # print(p.pid)#进程id

    #主进程等待子线程结束方法1:
    while len(multiprocessing.active_children())!=0:
        pass

    #主进程等待子线程结束方法2:
    # for x in process:
    #     x.join()

  





原文地址:https://www.cnblogs.com/bugoobird/p/13125830.html