一.线程和进程
1.操作系统中,线程是CPU调度和分派的基本单位,线程依存于程序中
2.操作系统中,进程是系统进行资源分配和调度的一个基本单位,一个程序至少有一个进程
3.一个进程由至少一个线程组成,线程组成进程
4.多进程、多进程实际是进程、线程、进程和线程的并发而不是并行,用来加快程序运行速度
5.Python既支持多线程,也支持多进程。
二.多线程threading
1.python3线程操作中常用模块:_thread和threading,其中一般都用threading模块
2.线程分为:内核线程:由操作系统内核创建和撤销;用户线程:不需要内核支持而在用户程序中实现的线程
3.Python中使用线程有两种方式:函数或者用类来包装线程对象
2. 创建线程
1 import threading 2 3 #def main():#定义一个存放多线程的函数 4 # print(threading.active_count())#获取已激活的线程数 5 # print(threading.enumerate()) # see the thread list查询线程信息 6 # print(threading.current_thread())#查询当前运行的线程 7 8 def thread_job():#定义一个线程的工作的函数 9 print('This is a thread of %s' % threading.current_thread()) 10 def main(): 11 thread = threading.Thread(target=thread_job,)#添加线程,参数为线程的目标(任务) 12 thread.start()#执行线程thread 13 14 if __name__ == '__main__':#在该程序中运行 15 main() 16 --------------------------------------- 17 This is a thread of <Thread(Thread-1, started 5664)>
1 #调用 _thread 模块中的start_new_thread()函数来产生新线程 2 #_thread.start_new_thread ( function, args[, kwargs] )(线程函数,函数的参数tuple,可选参数) 3 import _thread 4 import time 5 6 # 为线程定义一个函数 7 def print_time( threadName, delay): 8 count = 0 9 while count < 5: 10 time.sleep(delay) 11 count += 1 12 print ("%s: %s" % ( threadName, time.ctime(time.time()) )) 13 14 # 创建两个线程 15 try: 16 _thread.start_new_thread( print_time, ("Thread-1", 2, ) ) 17 _thread.start_new_thread( print_time, ("Thread-2", 4, ) ) 18 except: 19 print ("Error: 无法启动线程") 20 21 while 1: 22 pass 23 --------------------------------------------------------- 24 Thread-1: Thu May 17 17:13:01 2018 25 Thread-2: Thu May 17 17:13:03 2018 26 Thread-1: Thu May 17 17:13:03 2018 27 Thread-1: Thu May 17 17:13:05 2018 28 Thread-2: Thu May 17 17:13:07 2018 29 。。。
1 #从 threading.Thread 继承创建一个新的子类,并实例化后调用 start() 方法启动新线程,即它调用了线程的 run() 方法 2 import threading 3 import time 4 exitFlag = 0 5 class myThread (threading.Thread): 6 def __init__(self, threadID, name, counter): 7 threading.Thread.__init__(self) 8 self.threadID = threadID 9 self.name = name 10 self.counter = counter 11 def run(self): 12 print ("开始线程:" + self.name) 13 print_time(self.name, self.counter, 5) 14 print ("退出线程:" + self.name) 15 16 def print_time(threadName, delay, counter): 17 while counter: 18 if exitFlag: 19 threadName.exit() 20 time.sleep(delay) 21 print ("%s: %s" % (threadName, time.ctime(time.time()))) 22 counter -= 1 23 24 # 创建新线程 25 thread1 = myThread(1, "Thread-1", 1) 26 thread2 = myThread(2, "Thread-2", 2) 27 28 # 开启新线程 29 thread1.start() 30 thread2.start() 31 thread1.join() 32 thread2.join() 33 print ("退出主线程") 34 ------------------------------------------------ 35 开始线程:Thread-1 36 开始线程:Thread-2 37 Thread-1: Thu May 17 17:18:32 2018 38 Thread-2: Thu May 17 17:18:33 2018 39 Thread-1: Thu May 17 17:18:33 2018 40 。。。
3.join功能,控制执行顺序
1 import threading 2 import time 3 4 def thread_job():#定义一个线程 5 print('T1 start\n')#开始t1线程 6 for i in range(10):#设置10步 7 time.sleep(0.1)#任务间隔0.1秒,增加时耗 8 print('T1 finish\n')#结束t1线程 9 10 def T2_job():#又定义一个线程 11 print('T2 start\n')#开始 12 print('T2 finish\n')#结束 13 14 def main():#定义主程序 15 thread1 = threading.Thread(target=thread_job, name='T1')#添加线程t1 16 thread2 = threading.Thread(target=T2_job, name='T2')#添加线程t2 17 thread1.start()#开始运行t1 18 thread2.start()#开始运行t2 19 #使用join控制多个线程的执行顺序 20 thread2.join()#等到t2运行完再进行下一步 21 thread1.join()#到t1运行完再进行下一步 22 print('all done\n')#表明程序执行完 23 24 if __name__ == '__main__': 25 main() 26 -------------------------------------------------------- 27 T1 start 28 29 T2 start 30 31 T2 finish 32 33 T1 finish 34 35 all done
4.存储进程结果Queue,多线程调用的函数不能有返回值, 所以使用Queue存储多个线程运算的结果
1 #,将数据列表中的数据传入,使用四个线程处理,将结果保存在Queue中, 2 # 线程执行完后,从Queue中获取存储的结果 3 import threading 4 import time 5 from queue import Queue#导入队列模块 6 7 def job(l,q):#函数的参数是一个列表l和一个队列q 8 #函数的功能是,对列表的每个元素进行平方计算,将结果保存在队列中 9 for i in range(len(l)): 10 l[i] = l[i]**2 11 q.put(l)#把列表l存放进队列q中 12 13 def multithreading():#定义一个多线程函数 14 q = Queue()#创建一个空队列,用来保存返回值 15 threads = []#定义一个多线程列表 16 data = [[1,2,3],[3,4,5],[4,4,4],[5,5,5]]#初始化一个多维数据列表 17 for i in range(4):#定义四个线程 18 t = threading.Thread(target=job, args=(data[i], q)) 19 #创建一个线程,任务是job函数,被调用的job函数没有括号,只是一个索引,参数在后面 20 t.start()#开始t 21 threads.append(t)# #把每个线程append到线程列表中 22 for thread in threads: 23 thread.join()#分别join四个线程到主线程 24 results = []#定义一个空的列表results,将四个线运行后保存在队列中的结果返回给空列表results 25 for _ in range(4): 26 results.append(q.get()) #q.get()按顺序从q中拿出一个值 27 print(results) 28 29 if __name__ == '__main__': 30 multithreading() 31 ------------------------------------------------- 32 [[1, 4, 9], [9, 16, 25], [16, 16, 16], [25, 25, 25]]
1 import queue 2 import threading 3 import time 4 5 exitFlag = 0 6 7 class myThread (threading.Thread): 8 def __init__(self, threadID, name, q): 9 threading.Thread.__init__(self) 10 self.threadID = threadID 11 self.name = name 12 self.q = q 13 def run(self): 14 print ("开启线程:" + self.name) 15 process_data(self.name, self.q) 16 print ("退出线程:" + self.name) 17 18 def process_data(threadName, q): 19 while not exitFlag: 20 queueLock.acquire() 21 if not workQueue.empty(): 22 data = q.get() 23 queueLock.release() 24 print ("%s processing %s" % (threadName, data)) 25 else: 26 queueLock.release() 27 time.sleep(1) 28 29 threadList = ["Thread-1", "Thread-2", "Thread-3"] 30 nameList = ["One", "Two", "Three", "Four", "Five"] 31 queueLock = threading.Lock() 32 workQueue = queue.Queue(10) 33 threads = [] 34 threadID = 1 35 36 # 创建新线程 37 for tName in threadList: 38 thread = myThread(threadID, tName, workQueue) 39 thread.start() 40 threads.append(thread) 41 threadID += 1 42 43 # 填充队列 44 queueLock.acquire() 45 for word in nameList: 46 workQueue.put(word) 47 queueLock.release() 48 49 # 等待队列清空 50 while not workQueue.empty(): 51 pass 52 53 # 通知线程是时候退出 54 exitFlag = 1 55 56 # 等待所有线程完成 57 for t in threads: 58 t.join() 59 print ("退出主线程") 60 ------------------------------------------------------ 61 开启线程:Thread-1 62 开启线程:Thread-2 63 开启线程:Thread-3 64 Thread-3 processing One 65 Thread-2 processing Two 66 Thread-1 processing Three 67 Thread-3 processing Four 68 Thread-2 processing Five 69 退出线程:Thread-3 70 退出线程:Thread-2 71 退出线程:Thread-1 72 退出主线程
5.GIL(Global Interpreter Lock)全局解释锁:它确保任何时候都只有一个Python线程执行,导致了多线程无法利用多核。
实质上python中的多线程就是节省了i/o时间
1 # 测试 GIL 2 import threading 3 from queue import Queue 4 import copy 5 import time 6 7 def job(l, q):#传入一个列表和一个队列, 8 #操作:计算列表的总和,并把它传入到队列中 9 res = sum(l) 10 q.put(res) 11 12 def multithreading(l):#多线程函数 13 q = Queue() 14 threads = [] 15 for i in range(4):#同时执行4个线程 16 t = threading.Thread(target=job, args=(copy.copy(l), q), name='T%i' % i) 17 t.start()#t开始执行 18 threads.append(t)#线程列表中添加t 19 [t.join() for t in threads] 20 total = 0 21 for _ in range(4): 22 total += q.get() 23 print(total) 24 25 def normal(l):#计算列表的总值 26 total = sum(l) 27 print(total) 28 29 if __name__ == '__main__': 30 l = list(range(1000000)) 31 # 1.正常执行:一个列表扩展4倍,并打印出执行时间 32 s_t = time.time() 33 normal(l*4) 34 print('normal: ',time.time()-s_t) 35 36 #2.线程执行:建立四个线程执行 37 s_t = time.time() 38 multithreading(l) 39 print('multithreading: ', time.time()-s_t) 40 ------------------------------------------------------------- 41 1999998000000 42 normal: 0.24673938751220703 43 1999998000000 44 multithreading: 0.244584321975708
6.lock线程锁:lock在不同线程使用同一共享内存时(只允许一个线程修改数据),能够确保线程之间互不影响,但是阻止了多线程并行,也可能导致死锁
如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步
多线程和多进程最大的不同在于:多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线
程修改,因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了
1 #使用lock的方法是, 在每个线程执行运算修改共享内存之前,执行lock.acquire()将共享内存上锁, 2 # 确保当前线程执行时,内存不会被其他线程访问, 3 # 执行运算完毕后,使用lock.release()将锁打开, 保证其他的线程可以使用该共享内存。 4 5 import threading 6 7 def job1(): 8 global A, lock#使用线程锁 9 lock.acquire()#lock.acquire()将共享内存上锁 10 for i in range(10): 11 A += 1 12 print('job1', A) 13 lock.release()#lock.release()将锁打开, 保证其他的线程可以使用该共享内存 14 15 def job2(): 16 global A, lock 17 lock.acquire()#确保当前线程执行时,内存不会被其他线程访问 18 for i in range(10): 19 A += 10 20 print('job2', A) 21 lock.release()#保证其他的线程可以使用该共享内存 22 23 if __name__ == '__main__': 24 lock = threading.Lock()#创建一个线程锁 25 A = 0 26 t1 = threading.Thread(target=job1) 27 t2 = threading.Thread(target=job2) 28 t1.start() 29 t2.start() 30 t1.join() 31 t2.join() 32 ------------------------------------------------------------ 33 job1 1 34 job1 2 35 job1 3 36 job1 4 37 job1 5 38 job1 6 39 job1 7 40 job1 8 41 job1 9 42 job1 10 43 job2 20 44 job2 30 45 job2 40 46 job2 50 47 job2 60 48 job2 70 49 job2 80 50 job2 90 51 job2 100
1 #使用 Thread 对象的 Lock 和 Rlock 可以实现简单的线程同步 2 import threading 3 import time 4 class myThread (threading.Thread): 5 def __init__(self, threadID, name, counter): 6 threading.Thread.__init__(self) 7 self.threadID = threadID 8 self.name = name 9 self.counter = counter 10 def run(self): 11 print ("开启线程: " + self.name) 12 # 获取锁,用于线程同步 13 threadLock.acquire() 14 print_time(self.name, self.counter, 3) 15 # 释放锁,开启下一个线程 16 threadLock.release() 17 18 def print_time(threadName, delay, counter): 19 while counter: 20 time.sleep(delay) 21 print ("%s: %s" % (threadName, time.ctime(time.time()))) 22 counter -= 1 23 24 threadLock = threading.Lock() 25 threads = [] 26 27 # 创建新线程 28 thread1 = myThread(1, "Thread-1", 1) 29 thread2 = myThread(2, "Thread-2", 2) 30 31 # 开启新线程 32 thread1.start() 33 thread2.start() 34 35 # 添加线程到线程列表 36 threads.append(thread1) 37 threads.append(thread2) 38 39 # 等待所有线程完成 40 for t in threads: 41 t.join() 42 print ("退出主线程") 43 ------------------------------------------------------ 44 开启线程: Thread-1 45 开启线程: Thread-2 46 Thread-1: Thu May 17 17:25:58 2018 47 Thread-1: Thu May 17 17:25:59 2018 48 Thread-1: Thu May 17 17:26:00 2018 49 Thread-2: Thu May 17 17:26:02 2018 50 Thread-2: Thu May 17 17:26:04 2018 51 Thread-2: Thu May 17 17:26:06 2018 52 退出主线程
递归锁RLcok类的用法和Lock类一模一样,但它支持嵌套,在多个锁没有释放的时候一般会使用使用RLcok类
信号量(BoundedSemaphore类)用法和Lock类一模一样,但同时允许一定数量的线程更改数据
7.补充
三.多进程multiprocessing(或者说是多核)
1.多进程 Multiprocessing 和多线程 threading 类似,但是多核使用,用来弥补 threading 的一些劣势, 比如GIL
2.创建进程
1 import multiprocessing as mp 2 import threading as td 3 4 def job(a,d):#定义一个被线程和进程调用的函数 5 print('aaaaa') 6 7 if __name__=='__main__': 8 # 创建线程和进程 9 t1 = td.Thread(target=job,args=(1,2)) 10 p1 = mp.Process(target=job,args=(1,2)) 11 12 t1.start() 13 p1.start() 14 15 t1.join() 16 p1.join() 17 ------------------------------------------------------- 18 aaaaa 19 aaaaa
3.Queue:将每个核或线程的运算结果放在队里中, 等到每个线程或核运行完毕后再从队列中取出结果, 继续加载运算。
1 import multiprocessing as mp 2 3 #调用的函数不能有返回值 4 def job(q): 5 res = 0 6 for i in range(1000): 7 res += i+i**2+i**3 8 q.put(res) # queue把结果放到q中 9 10 if __name__ == '__main__': 11 q = mp.Queue() 12 p1 = mp.Process(target=job, args=(q,)) 13 #要加逗号,否则TypeError: 'Queue' object is not iterable 14 p2 = mp.Process(target=job, args=(q,)) 15 p1.start() 16 p2.start() 17 p1.join() 18 p2.join() 19 res1 = q.get()#获取q中的结果 20 res2 = q.get() 21 print(res1+res2) 22 -------------------------------------------------------------- 23 499667166000
4.效率对比
1 #多线程与多进程的效率对比 2 import multiprocessing as mp 3 import threading as td 4 import time 5 6 def job(q): 7 res = 0 8 for i in range(1000000): 9 res += i+i**2+i**3 10 q.put(res) # queue 11 12 def multicore(): 13 q = mp.Queue() 14 p1 = mp.Process(target=job, args=(q,)) 15 p2 = mp.Process(target=job, args=(q,)) 16 p1.start() 17 p2.start() 18 p1.join() 19 p2.join() 20 res1 = q.get() 21 res2 = q.get() 22 print('multicore:' , res1+res2) 23 24 def normal():#普通运算 25 res = 0 26 for _ in range(2): 27 for i in range(1000000): 28 res += i+i**2+i**3 29 print('normal:', res) 30 31 def multithread(): 32 q = mp.Queue() 33 t1 = td.Thread(target=job, args=(q,)) 34 t2 = td.Thread(target=job, args=(q,)) 35 t1.start() 36 t2.start() 37 t1.join() 38 t2.join() 39 res1 = q.get() 40 res2 = q.get() 41 print('multithread:', res1+res2) 42 43 if __name__ == '__main__': 44 st = time.time() 45 normal() 46 st1= time.time() 47 print('normal time:', st1 - st) 48 49 st2 = time.time() 50 multithread() 51 st3 = time.time() 52 print('multithread time:', st3 - st2) 53 54 st4 = time.time() 55 multicore() 56 st5 = time.time() 57 print('multicore time:', st5-st4) 58 ----------------------------------------------------------------- 59 normal: 499999666667166666000000 60 normal time: 1.8749690055847168 61 multithread: 499999666667166666000000 62 multithread time: 1.583195686340332 63 multicore: 499999666667166666000000 64 multicore time: 1.2573533058166504
5.进程池pool
1 #进程池就是我们将所要运行的东西,放到池子里,Python会自行解决多进程的问题 2 import multiprocessing as mp 3 4 def job(x): 5 return x*x 6 7 def multicore(): 8 pool = mp.Pool(processes=2)#创建进程池,定义调用2个cpu核 9 # 让池子对应某一个函数,我们向池子里丢数据,池子就会返回函数返回的值 10 # Pool和之前的Process的不同点是丢向Pool的函数有返回值,而Process的没有返回值 11 12 res = pool.map(job, range(10)) 13 #用map()获取结果,在map()中需要放入函数和需要迭代运算的值,然后它会自动分配给CPU核,返回结果 14 print(res) 15 16 res = pool.apply_async(job, (2,)) 17 # apply_async()返回结果,只能传递一个值,它只会放入一个核进行运算,但是传入值时要注意是可迭代的, 18 # 所以在传入值后需要加逗号, 同时需要用get()方法获取返回值 19 print(res.get()) 20 21 # 将apply_async()放入迭代器中,来输出多个结果 22 multi_res =[pool.apply_async(job, (i,)) for i in range(10)] 23 print([res.get() for res in multi_res]) 24 25 if __name__ == '__main__': 26 multicore() 27 -------------------------------------------------- 28 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 29 4 30 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
6.进程锁lock
1 # 共享内存 2 # import multiprocessing as mp 3 # 4 # # Value数据存储在一个共享的内存表中 5 # value1 = mp.Value('i', 0) 6 # value2 = mp.Value('d', 3.14) 7 # #d和i参数用来设置数据类型的 8 # 9 # #Array类,可以和共享内存交互,来实现在进程之间共享数据。 10 # array = mp.Array('i', [1, 2, 3, 4]) 11 # # 它只能是一维的,不能是多维的 12 13 #进程锁lock 14 import multiprocessing as mp 15 import time 16 17 def job(v, num, l): 18 l.acquire()#设置进程锁的使用,保证运行时一个进程的对锁内内容的独占 19 for _ in range(10): 20 time.sleep(0.1) 21 v.value += num# v.value获取共享变量值 22 print(v.value) 23 l.release()#进程锁保证了进程p1的完整运行,然后才进行了进程p2的运行 24 25 def multicore(): 26 l = mp.Lock()# 定义一个进程锁 27 v = mp.Value('i', 0) # 定义共享变量 28 # 设定不同的number看如何抢夺内存 29 p1 = mp.Process(target=job, args=(v, 1, l)) 30 p2 = mp.Process(target=job, args=(v, 3, l)) 31 p1.start() 32 p2.start() 33 p1.join() 34 p2.join() 35 36 if __name__ == '__main__': 37 multicore() 38 ------------------------------------------------- 39 1 40 2 41 3 42 4 43 5 44 6 45 7 46 8 47 9 48 10 49 13 50 16 51 19 52 22 53 25 54 28 55 31 56 34 57 37 58 40
来源:https://www.cnblogs.com/yu-liang/p/9026697.html