多线程
day 34
一、多线程
1、什么是线程
线程即程序的执行线路,相当于一条流水线,它包含了程序的具体执行步骤。
多线程(即多个控制线程)的概念是,在一个进程中存在多个控制线程,多个控制线程共享该进程的地址空间,相当于一个车间内有多条流水线,都共用一个车间的资源。
2、线程与进程之间的关系
进程中包含了运行该程序的所需要的所有资料,进程是一个资源单位,线程是CPU的最小执行单位,每一个进程一旦被创建,就默认开启了一条线程,称之为主线程。一个进程可以包含多个线程,进程包含线程,线程依赖进程。
3、为什么使用线程
为了体高程序的效率(使cpu在一个进程内进行切换),相比于多进程而已,多线程占用更少的操作系统资源。
4、开启多线程的两种方式:
# 开启线程的第一种方式 from threading import Thread from multiprocessing import Process def task(): print("threading running!") t1 = Thread(target=task) t1.start() print("over") # 第二种方式 class MyThread(Thread): def run(self): print("子线程 running....") MyThread().start() print("over2") # 对比进程启动时间 # from multiprocessing import Process # # def task(): # print("threading running!") # if __name__ == '__main__': # # t1 = Process(target=task) # t1.start() # # print("over")
5、进程和线程的区别: 1.进程对于操作系统的资源耗费非常高,而线程非常低(比进程低10-100倍); 2.在同一个进程中,多个线程之间资源是共享的。
###线程与进程对比示例一: from threading import Thread from multiprocessing import Process import os def work(): print('hello') if __name__ == '__main__': #在主进程下开启线程 t=Thread(target=work) t.start() print('主线程/主进程') ''' 打印结果: hello 主线程/主进程 ''' #在主进程下开启子进程 t=Process(target=work) t.start() print('主线程/主进程') ''' 打印结果: 主线程/主进程 hello ''' ###线程与进程对比示例二: from multiprocessing import Process from threading import Thread import time def task(): # print("子进程任务....") pass # 100个进程时间统计 # if __name__ == '__main__': # start = time.time() # ps = [] # for i in range(100): # p = Process(target=task) # p.start() # ps.append(p) # for p in ps: # p.join() # print(time.time()-start) #100个线程 start = time.time() ts = [] for i in range(100): t = Thread(target=task) t.start() ts.append(t) for t in ts: t.join() print(time.time()-start)
6、守护线程
守护线程会等待主线程运行完毕后被销毁
#1.对主进程来说,运行完毕指的是主进程代码运行完毕 #2.对主线程来说,运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕,主线程才算运行完毕 #1 主进程在其代码结束后就已经算运行完毕了(守护进程在此时就被回收),然后主进程会一直等非守护的子进程都运行完毕后回收子进程的资源(否则会产生僵尸进程),才会结束, #2 主线程在其他非守护线程运行完毕后才算运行完毕(守护线程在此时就被回收)。因为主线程的结束意味着进程的结束,进程整体的资源都将被回收,而进程必须保证非守护线程都运行完毕后才能结束。 from threading import Thread # # import time # # def task(): # print("sub thread run....") # time.sleep(3) # print("sub thread over....") # # t = Thread(target=task) # t.setDaemon(True) # t.start() # # # t = Thread(target=task) # t.setDaemon(True) # t.start() # # print("over!") from threading import Thread import time def task(): print("子线程运行。。。") time.sleep(1) print("子线程结束。。。") t = Thread(target=task) t.setDaemon(True) t.start() # time.sleep(0.1) print("over")
7、线程中常用的属性
# Thread实例对象的方法 # isAlive(): 返回线程是否活动的。 # getName(): 返回线程名。 # setName(): 设置线程名。 # threading模块提供的一些方法: # threading.currentThread(): 返回当前的线程变量。 # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。 # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。 from threading import Thread,current_thread,enumerate,active_count import os def task(): print("running..") # print(os.getpid()) # print(current_thread()) print(active_count()) t1 = Thread(target=task) t1.start() # print(t1.is_alive()) # print(t1.isAlive()) # print(t1.getName()) # 获取所有线程对象列表 print(enumerate()) # 获取当前线程对象 print(current_thread()) # 获取当前正在运行的线程个数 print(active_count()) # t2 = Thread(target=task) # t2.start()
8、线程互斥锁
当多个进程或线程同时修改同一份数据时,可能会造成数据的错乱,所以必须要加锁。
锁的使用方法与进程中锁的使用方法完全相同。
import time from threading import Thread,Lock lock =Lock() a = 100 def task(): lock.acquire() global a temp = a - 1 time.sleep(0.01) a = temp lock.release() ts = [] for i in range(100): t = Thread(target=task) t.start() ts.append(t) for t in ts: t.join() print(a)
9、信号量
a、定义:信号量也是一种锁,他的特点是可以设置一个数据可以被几个线程(进程)共享。 b、与普通锁的区别:
普通锁一旦加锁,则意味着这个数据在同一时间只能被一个线程使用;而信号量可以让这个数据在统一时间被限定个数的多个进程使用。
from threading import Semaphore,Thread,current_thread import time,random sem = Semaphore(3) def task(): sem.acquire() print("%s run..." % current_thread()) time.sleep(3) sem.release() for i in range(10): t = Thread(target=task) t.start()
二、进程补充(守护进程的使用)
import time,random from multiprocessing import Process,JoinableQueue def eat_hotdog(name,q): while True: res = q.get() print("%s吃了%s" % (name,res)) time.sleep(random.randint(1,2)) q.task_done() #记录已经被处理的数据的数量 def make_hotdog(name,q): for i in range(1,6): time.sleep(random.randint(1, 2)) print("%s生产了第%s个热狗" % (name,i)) res = "%s的%s个热狗" % (name,i) q.put(res) # q.put(None) if __name__ == '__main__': q = JoinableQueue() #生产者1 c1 = Process(target=make_hotdog,args=("万达热狗店",q)) c1.start() #生产者2 c2 = Process(target=make_hotdog, args=("老男孩热狗店", q)) c2.start() # 消费者 p2 = Process(target=eat_hotdog,args=("思聪",q)) p2.daemon = True # 如果只是加这行代码,有可能会出现,热狗没吃完程序就结束了的情况,因此需要加上后面的p.join() p2.start() # 首先保证生产者全部产完成 c1.join() c2.join() # 保证队列中的数据全部被处理了 q.join() # 明确生产方已经不会再生成数据了 # 等待放入队列中的所有数据被取完后再往下运行