1 进程和线程是操作系统的基本概念,计算机是由硬件和软件组成。硬件中的CPU是计算机的核心,他承担计算机的所有任务。
2 操作系统是运行在硬件上的软件,是计算机的管理者,他负责资源的管理和分配、任务的调度。
3 程序是运行在系统上的具有某种功能的软件,比如浏览器、音乐、软件等。
4 每次执行程序的时候,都会完成一定的功能,比如说浏览器帮我们打开网页,为了保证其独立性,就需要一个专门的管理和控制执行程序
5 的数据结构--进程控制块。
6 进程就是一个程序在一个数据集上的一次动态执行过程。进程一般由程序、数据集、进程控制块三部分组成。
7 我们编写的程序用来描述进程要完成哪些功能以及如何完成;数据集则是程序在执行过程中所需要使用的资源;进程控制块用来记录进程的
8 外部特征,描述进程的执行变化过程,系统可以利用他来控制和管理进程,他是系统感知进程存在的唯一标志。
9 线程是操作系统能够进行运算调度的最小单位。他被包含在进程之中,是进程中的实际运作单位。
10 一个线程指的是进程中一个单一顺序的控制流。
11 一个进程中可以并发多个线程,每条线程并行执行不同的任务。
12
13 1.进程是不活泼的,进程从来不执行任何东西,他只是线程的容器,若要是进程完成某种操作,他必须有一个在他环境中运行的线程,此线程负责
14 执行包含在进程地址空间中的代码。
15 2.创建一个进程时,操作系统会自动创建这个进程的第一个线程,称为主线程。此后该线程可以创建其他的线程。
16 3.线程与进程的关系:线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间,当进程退出时,该进程产生的
17 线程都会被强制清楚并退出。线程可与同一进程的其他线程共享进程所拥有的全部资源,但是其本身基本上不拥有系统资源,只拥有一点在运行中必不可少的信息(如程序计数器、一组寄存器和栈)。
18
19 线程与进程的区别:
20 进程:对各种资源管理的集合;
21 线程:操作系统最小的调度单位,是一串指令的集合。
22
23 进程中第一个线程是主线程,主线程创建其他线程,其他线程也可以创建线程,线程之间是平等的
24 进程有父进程、子进程,独立的内存空间,唯一的进程标识符pid
25
26 启动线程比启动进程快。运行进程和运行线程速度上是一样的,没有可比性。
27 线程共享内存空间,进程的内存是独立的
28
29 父进程生成子进程,相当于克隆一份内存空间。进程之间不能直接访问
30 创建新线程很简单,创建新进程需要对其父进程进行一次克隆
31 一个线程可以控制和操作同一进程里的其他线程,但是进程只能操作子进程
32
33 同一个进程之间的线程之间可以直接通信
34 两个进程想通信必须通过一个中间代理来实现
35
36 进程的特性:
37 动态性:进程的实质是程序的一次执行过程,进程是动态产生、动态消亡的
38 并发性:任何进程都可以同其他进程一起并发执行
39 独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位
40 异步性:每个进程都以相互独立、不可预知的速度向前推进
41
42 进程由程序、数据和进程控制块三部分组成
43
44 多任务的实现有3种方式:
45 多进程模式
46 多线程模式
47 多进程+多线程模式
48
49
50 # 直接调用
51 import threading
52 import time
53 def run(n):
54 # time.sleep(2)
55 print('task', n)
56 t1=threading.Thread(target=run,args=('t1',))
57 t2=threading.Thread(target=run,args=('t2',))
58 t1.start()
59 t2.start()
60
61 # task t1
62 # task t2
63
64
65 继承式调用
66 import threading
67 class MyThread(threading.Thread):
68 def __init__(self,n):
69 super(MyThread,self).__init__()
70 self.n=n
71 def run(self):
72 print('这种方式函数名必须是run,写死的',self.n)
73 t1=MyThread('t1')
74 t2=MyThread('t2')
75 t1.start()
76 t2.start()
77
78 # 这种方式函数名必须是run,写死的 t1
79 # 这种方式函数名必须是run,写死的 t2
80
81
82
83 使用传统编程看执行任务花费的时间
84 import time
85 import threading
86
87 def task1():
88 time.sleep(5)
89 print('任务一完成',time.ctime())
90 def task2():
91 time.sleep(5)
92 print('任务二完成',time.ctime())
93 print('执行任务前打印当前时间',time.ctime())
94 task1()
95 task2()
96 print('执行结束,记录结束时间',time.ctime())
97
98 # 执行任务前打印当前时间 Sun Mar 10 08:21:49 2019
99 # 任务一完成 Sun Mar 10 08:21:54 2019
100 # 任务二完成 Sun Mar 10 08:21:59 2019
101 # 执行结束,记录结束时间 Sun Mar 10 08:21:59 2019
102
103
104
105 采用多线程实行并行处理,查看执行同样任务所花费的时间
106 import time
107 import threading
108
109 def task1():
110 time.sleep(3)
111 print('任务一完成',time.ctime())
112 def task2():
113 time.sleep(3)
114 print('任务二完成',time.ctime())
115 print('执行任务前打印当前时间',time.ctime())
116 t1=threading.Thread(target=task1)
117 t2=threading.Thread(target=task2)
118 t1.start()
119 t2.start()
120 t1.join()
121 t2.join()
122 print('执行结束,打印当前时间')
123
124 # 执行任务前打印当前时间 Sun Mar 10 08:28:44 2019
125 # 任务二完成 任务一完成 Sun Mar 10 08:28:47 2019
126 # Sun Mar 10 08:28:47 2019
127 # 执行结束,打印当前时间
128
129
130
131 任何进程默认就会启动一个线程,我们把该线程称为主线程,主线程又可以启动新的线程,Python的threading模块有个current_thread()函数,
132 他永远返回当前线程的实例。主线程实例的名字叫MainThread,子线程的名字在创建线程实例时指定,这里用hello命名子线程。
133 名字仅仅用来在打印时显示,完全没有其他意义,如果不起名字,python就会自动给线程命名为Thread-1,Thread-2...
134 import time
135 import threading
136
137 def loop():
138 print('线程%s正在执行。。。'%threading.current_thread().name)
139 n=0
140 while n<5:
141 n+=1
142 print('线程%s>>>%s'%(threading.current_thread().name,n))
143 time.sleep(2)
144 print('线程%s结束'%threading.current_thread().name)
145 print('主线程%s正在执行。。。'%threading.current_thread().name)
146 # 给线程起名hello
147 t=threading.Thread(target=loop,name='hello')
148 t.start()
149 t.join()
150 print('主线程%s结束'%threading.current_thread().name)
151
152 # 主线程MainThread正在执行。。。
153 # 线程hello正在执行。。。
154 # 线程hello>>>1
155 # 线程hello>>>2
156 # 线程hello>>>3
157 # 线程hello>>>4
158 # 线程hello>>>5
159 # 线程hello结束
160 # 主线程MainThread结束
161
162
163
164 当我们使用setDaemon(True)方法时,设置子线程为守护线程时,主线程一旦执行结束,则全部线程全部被终止执行,
165 可能出现的情况就是,子线程的任务还没有完全结束,就被迫停止。不设置的话默认为setDaemon(False),
166 如果你设置一个线程为守护线程,就表示这个线程是不重要的,在进程退出的时候,不用等待这个线程退出。
167 import time
168 import threading
169
170 def task():
171 print('start fun',time.ctime())
172 time.sleep(2)
173 print('end fun',time.ctime())
174 t1=threading.Thread(target=task)
175 print('线程名字',t1.getName(),time.ctime()) #显示实例线程名字
176 t1.setDaemon(True) # 设置t1为守护线程
177 t1.start()
178 time.sleep(1)
179 print(threading.current_thread().name,time.ctime()) # 主线程执行结束,则全部终止
180
181 # 线程名字 Thread-1 Sun Mar 10 16:58:14 2019
182 # start fun Sun Mar 10 16:58:14 2019
183 # MainThread Sun Mar 10 16:58:15 2019
184
185
186
187
188 多进程与多线程最大的不同在于,多进程中,同一个变量,各自有一份拷贝在每个进程中,互不影响,而多线程中,所有变量都由所有线程
189 共享。所以任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时修改一个变量,把内容给改乱了。
190
191
192
193 # 没有锁
194 import threading
195
196 balance=0
197 def task(n):
198 global balance
199 balance+=n
200 balance-=n
201 def task2(arg,n):
202 while arg>0:
203 # lock.acquire()#获得锁
204 task(n)
205 arg-=1
206
207 t1=threading.Thread(target=task2,args=(880000,5))
208 t2=threading.Thread(target=task2,args=(970000,6))
209 t1.start()
210 t2.start()
211 t1.join()
212 t2.join()
213 print(balance)
214
215
216
217 import threading
218
219 balance=0
220 lock=threading.RLock()
221 def task(n):
222 global balance
223 balance+=n
224 balance-=n
225 def task2(arg,n):
226 while arg>0:
227 lock.acquire()#获得锁
228 try:
229 task(n)
230 finally:
231 lock.release()
232 arg-=1
233
234 t1=threading.Thread(target=task2,args=(1880000,5))
235 t2=threading.Thread(target=task2,args=(1770000,6))
236 t1.start()
237 t2.start()
238 t1.join()
239 t2.join()
240 print(balance)
241
242 当多个线程同时执行lock.acquire()时,只有一个线程能够成功地获取锁,然后执行代码,其他线程就继续等待直到获得锁为止。
243 获得锁的线程用完后一定要释放锁,否则那些苦苦等待的线程将永远等待下去,称为死线程。
244 锁的好处就是确保了某段代码只能由一个线程从头到尾的执行。坏处当然也很多,首先是阻止了多线程并发执行,包含锁的某段代码实际上
245 只能以单线程的模式执行,效率就大大下降了。其次,由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会
246 造成死锁,导致多个线程全部挂起,既不能执行,也无法结束,只能靠操作系统强制终止。
247
248 在python中,不能利用多线程实现多核任务,但可以通过多进程实现多核任务。
249
250 import time
251 import threading
252
253 globals_num=0
254 #lock=threading.RLock()
255 def Func():
256 # lock.acquire()
257 global globals_num
258 globals_num+=1
259 time.sleep(1)
260 print(globals_num)
261 # lock.release()
262 for i in range(10):
263 t=threading.Thread(target=Func)
264 t.start()
265
266 # 1010
267 #
268 # 10
269 # 101010
270 #
271 # 10
272 #
273 # 101010
274
275
276
277 import time
278 import threading
279
280 globals_num=0
281 lock=threading.RLock()
282 def Func():
283 lock.acquire()
284 global globals_num
285 globals_num+=1
286 time.sleep(1)
287 print(globals_num)
288 lock.release()
289 for i in range(10):
290 t=threading.Thread(target=Func)
291 t.start()
292
293 # 1
294 # 2
295 # 3
296 # 4
297 # 5
298 # 6
299 # 7
300 # 8
301 # 9
302 # 10
303
304 在多线程环境下,每个线程都有自己的数据。一个线程使用自己的局部变量比使用全局变量好,因为局部变量只有线程自己能看见,不会影响
305 其他线程,而全局变量的修改必须加锁。
306
307 ThreadLocal,很多地方叫做线程本地变量,也有些地方叫做线程本地存储,其实意思差不多。ThreadLocal为变量在每个线程中都创建了
308 一个副本,那么每个线程可以访问自己内部的副本变量。
309 ThreadLocal最常用的地方就是为每个线程绑定一个数据库连接,HTTP请求,用户身份信息等,这样一个线程的所有调用到的处理函数都可以
310 非常方便地访问这些资源。
311
312 在新的线程中local_data并没有x属性,并且在新线程中的赋值并不会影响到其他线程。
313 去掉local_data=Widgt()的注释,local_data就变成了线程共享的变量。
314
315 import threading
316
317 class Widgt(object):
318 pass
319
320 def test():
321 local_data=threading.local()
322 # local_data=Widgt()
323 local_data.x=1
324 def thread_func():
325 print('Has x in new thread:%s'%hasattr(local_data,'x'))#hasattr(实例名,属性名)判断对象中是否存在该属性
326 local_data.x=2
327 print('Has x in new thread:%s'%hasattr(local_data,'x'))
328 print('x in pre thread is %s'%local_data.x)
329 t=threading.Thread(target=thread_func)
330 t.start()
331 t.join()
332 print('x in pre thread is %s'%local_data.x)
333 if __name__=='__main__':
334 test()
335
336 # Has x in new thread:False
337 # Has x in new thread:True
338 # x in pre thread is 2
339 # x in pre thread is 1
340
341
342
343
344 创建全局ThreadLocal对象
345 import threading
346
347 local_school=threading.local()
348 def process_student():
349 #获取当前线程关联的student
350 std=local_school.student
351 print('Hello,%s(in %s)'%(std,threading.current_thread().name))
352 def process_thread(name):
353 #绑定ThreadLocal的student
354 local_school.student=name
355 process_student()
356 t1=threading.Thread(target=process_thread,args=('Alice',),name='Thread-A')
357 t2=threading.Thread(target=process_thread,args=('Bob',),name='Thread-B')
358 t1.start()
359 t2.start()
360 t1.join()
361 t2.join()
362
363 # Hello,Alice(in Thread-A)
364 # Hello,Bob(in Thread-B)
365
366
367
368 multiprocessing模块提供了一个Process类来代表一个进程对象,multiprocessing模块就是跨平台版本的多进程模块。
369 import time
370 import multiprocessing
371
372 def add(number,value,lock):
373 lock.acquire()
374 try:
375 print('init add{0} number={1}'.format(value,number))
376 for i in range(1,6):
377 number+=value
378 time.sleep(1)
379 print('add{0} number={1}'.format(value,number))
380 except Exception as e:
381 raise e
382 finally:
383 lock.release()
384 if __name__=='__main__':
385 lock=multiprocessing.Lock()
386 number=0
387 p1=multiprocessing.Process(target=add,args=(number,1,lock))
388 p2=multiprocessing.Process(target=add,args=(number,3,lock))
389 p1.start()
390 p2.start()
391 print('main end')
392
393 # main end
394 # init add1 number=0
395 # add1 number=1
396 # add1 number=2
397 # add1 number=3
398 # add1 number=4
399 # add1 number=5
400 # init add3 number=0
401 # add3 number=3
402 # add3 number=6
403 # add3 number=9
404 # add3 number=12
405 # add3 number=15
406
407
408
409
410 下面的例子演示了启动一个子进程并等待其结束
411
412 from multiprocessing import Process
413 import os
414
415 #子进程要执行的代码
416 def run_proc(name):
417 print('Run child process %s(%s)...'%(name,os.getpid()))
418 if __name__=='__main__':
419 print('Parent process %s.'%(os.getpid()))
420 p=Process(target=run_proc,args=('test',))
421 print('Child process will start.')
422 p.start() #启动进程
423 p.join() #等待子进程执行结束后再往下执行,通常用于进程间的同步
424 print('Child process end.')
425
426 # Parent process 6012.
427 # Child process will start.
428 # Run child process test(8724)...
429 # Child process end.
430
431
432
433
434
435 Pool类 在使用Python进行系统管理时,特别是同时操作多个文件目录或者远程控制多台主机,并行操作可以节约大量的时间。如果操作
436 对象数目不大时,还可以直接使用Process类动态的生成多个进程,十几个还好,但是如果上百个甚至更多,那手动去限制进程数量就显得
437 特别的繁琐,此时进程池就派上用场了。
438 Pool类可以提供指定数量的进程共用户调用,当有新的需求提交到Pool中时,如果池还没满,就会创建一个新的进程来执行请求。如果
439 池满,请求就会告知等待,直到池中有进程结束,才会创建新的进程来执行这些请求。
440 下面介绍multiprocessing模块下的Pool类下的几个方法
441 apply()
442 apply(func[,args=()[,kwds={}]])该函数用于传递不定参数,主进程会被阻塞直到函数执行结束(不建议使用,并且3.x以后不再出现)
443 apply_async()
444 apply_async(func[,args=()[,kwds={}[,callback=None]]])与apply用法一样,但他是非阻塞且支持结果返回进行回调
445 map()
446 map(func,iterable[,chunksize=None]) Pool类中的map方法,与内置的map函数用法基本一致,它会使进程阻塞直到返回结果。注意:第二个
447 参数虽然是迭代器,但在实际使用中,必须在整个队列都就绪后,程序才会运行子进程。
448 close()关闭进程池,使其不再接收新的任务。
449 terminate()结束工作进程,不再处理未完成的任务。
450 join()主进程阻塞等待子进程的退出,join方法必须在close或terminate之后使用。
451
452 from multiprocessing import Pool
453 import os
454 import time
455 import random
456
457 def long_time_task(name):
458 print('运行任务%s(%s)...'%(name,os.getpid()),time.ctime()) # os.getpid()获得当前进程的进程号
459 start=time.time()
460 time.sleep(random.random()*3)
461 end=time.time()
462 print('任务%s运行%0.2f秒'%(name,(end-start)),time.ctime())
463 if __name__=='__main__':
464 print('Parent process %s.'%os.getpid(),time.ctime())
465 p=Pool(4) # 创建拥有4个进程数量的进程池
466 for i in range(5):
467 p.apply_async(long_time_task,args=(i,))
468 print('Waiting for all subprocesses done...',time.ctime())
469 p.close()
470 p.join()
471 print('All subprocesses done.',time.ctime())
472
473 # Parent process 9704. Mon Mar 11 16:14:31 2019
474 # Waiting for all subprocesses done... Mon Mar 11 16:14:31 2019
475 # 运行任务0(5832)... Mon Mar 11 16:14:31 2019
476 # 运行任务1(7004)... Mon Mar 11 16:14:31 2019
477 # 运行任务2(6956)... Mon Mar 11 16:14:31 2019
478 # 运行任务3(1632)... Mon Mar 11 16:14:31 2019
479 # 任务3运行1.00秒 Mon Mar 11 16:14:32 2019
480 # 运行任务4(1632)... Mon Mar 11 16:14:32 2019
481 # 任务1运行1.98秒 Mon Mar 11 16:14:33 2019
482 # 任务2运行2.36秒 Mon Mar 11 16:14:34 2019
483 # 任务0运行2.98秒 Mon Mar 11 16:14:34 2019
484 # 任务4运行2.01秒 Mon Mar 11 16:14:34 2019
485 # All subprocesses done. Mon Mar 11 16:14:35 2019
486
487
488
489
490 Process之间肯定是需要通信的,操作系统提供了很多机制来实现进程间的通信。Python的multiprocessing模块包装了底层的机制,
491 提供了Queue,Pipes等多种方式来交换数据。我们以Queue为例,在父进程中创建两个子进程,一个往Queue里写数据,另一个从Queue里
492 读取数据。
493
494 from multiprocessing import Process,Queue
495 import os
496 import time
497 import random
498
499 # 写数据进程执行的代码
500 def write(q):
501 print('Process to write:%s'%os.getpid())
502 for value in ['A','B','C']:
503 print('Put %s to queue...'%value)
504 q.put(value)
505 time.sleep(random.random())
506 # 读数据进程执行的代码
507 def read(q):
508 print('Process to read:%s'%os.getpid())
509 while True:
510 value=q.get(True)
511 print('Get %s from queue.'%value)
512 if __name__=='__main__':
513 q=Queue() # 父进程创建Queue,并创建给子进程
514 w=Process(target=write,args=(q,))
515 r=Process(target=read,args=(q,))
516 w.start() # 启动子进程pw,写入
517 r.start() # 启动子进程pr,读取
518 w.join()
519 r.terminate() # r
520
521 # Process to write:9048
522 # Put A to queue...
523 # Process to read:1580
524 # Get A from queue.
525 # Put B to queue...
526 # Get B from queue.
527 # Put C to queue...
528 # Get C from queue.
529
530
531
532
533 Queue是python标准库中的线程安全的队列(FIFO)实现,提供了一个适用于多线程编程的先进先出的数据结构,即队列,用来在生产者
534 和消费者线程之间的信息传递。基本FIFO队列 class queue.Queue(maxsize=0)
535 FIFO即First in First out ,先进先出。Queue提供了一个基本的FIFO容器,使用方法很简单,maxsize是一个整数,指明了队列中能存放的
536 数据个数的上限。一旦达到了上限,插入会导致阻塞,直到队列中数据被消费掉。如果maxsize小于或者等于0,队列大小没有限制。
537
538 import queue
539
540 q=queue.Queue()
541 for i in range(5):
542 q.put(i)
543 while not q.empty():
544 print(q.get())
545
546 # 0
547 # 1
548 # 2
549 # 3
550 # 4
551
552
553
554 LIFO队列 即 last in first out ,后进先出
555 class queue.LifoQueue(maxsize=0)
556
557 import queue
558
559 q=queue.LifoQueue(maxsize=0)
560 for i in range(5):
561 q.put(i)
562 while not q.empty():
563 print(q.get())
564
565 # 4
566 # 3
567 # 2
568 # 1
569 # 0