进程线程

简介:

多进程

创建子进程

import os
print('Process(%s) start...' % os.getpid())
pid = os.fork()
if pid == 0:
    print("I am child process (%s) and my parent is %s.' %(os.getpid(), os.getppid()))
else:
    print('I (%s) just created a child process(%s).' %(os.getpid(), pid))

python创建子进程是封装了系统的fork调用。

 

python中创建跨平台的多进程应用,使用multiprocessing模块。

from multiprocessing import Process
import os

def run_proc(name):
    print("Run child process %s (%s)...' % (name, os.getpid()))
    
if __name__='__main__':
    print("Parent process %s.' % os.getpid())
    p = Process(target=run_proc, args = ('test', ))
    print('child process will start.')
    p.start()
    p.join()
    print('child process end.')

创建子进程,只需要传入一个执行函数和函数的参数,创建一个process实例用start()方法启动,比fork()简单。join()可以等待子进程结束后再继续往下运行,用于进程间的同步。

 

Pool -- 进程池

from multiprocessing import Pool
import os, time, random

def long_time_task(name):
    print("Run task %s (%s)...' % (name, os.getpid()))
    start = time.time()
    time.sleep(random.random() * 3)
    end = time.time()
    print("Task %s run %0.2f seconds.' % (name, (end - start)))
    
    
if __name__ == '__main__':
    print("Parent process %s.' % os.getpid())
    p = Pool(4)
    for i in range(5):
        p.apply_async(long_time_task, args=(i,))
    print("Waiting for all subprocesses done...')
    p.close()
    p.join()
    print('All subprocesses done.')

Pool对象调用join()会等待所有子进程执行完毕,调用join()之前必须先调用close(),调用close()之后就不能继续添加新的process了。

 

subprocess -- 启动一个子进程,控制其输入和输出。

子进程需要输入则使用communicate()方法

 

进程间通信 -- Queue  Pipes

在父进程中创建两个子进程,一个向queue写,一个从queuq读。

from multiprocessing import Process, Queue
import os, time, random

def write(q):
    print("Process to write: %s' % os.getpid())
    for value in ['A', 'B', 'C']:
        print('put %s to queue...' % value)
        q.put(value)
        time.sleep(random.random())
        
def read(q):
    print('Process to read: %s' % os.getpid())
    while True:
        value = q.get(True)
        print('Get %s from queue.' % value)

        
if __name__ == '__main__':
    q = Queue()
    pw = Process(target=write, args=(q,))
    pr = Process(target=read, args=(q,))
    pw.start()
    pr.start()
    pw.join()
    pr.terminate()

 

多线程

多线程模块 _threadthreadingthreading是高级模块,对_thread进行了封装。

 启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import  time, threading # 新线程执行的代码:def loop():
     print ( 'thread %s is running...'  %  threading.current_thread().name)
     =  0
     while  n <  5 :
         =  +  1
         print ( 'thread %s >>> %s'  %  (threading.current_thread().name, n))
         time.sleep( 1 )
     print ( 'thread %s ended.'  %  threading.current_thread().name)
 
print ( 'thread %s is running...'  %  threading.current_thread().name)
=  threading.Thread(target = loop, name = 'LoopThread' )
t.start()
t.join()
print ( 'thread %s ended.'  %  threading.current_thread().name)

Lock

线程锁 -- 

线程是共享进程数据的,为了保证数据的安全性,必须要使用线程锁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import  time, threading
 
balance  =  0
lock = threading.Lock()
 
def  change_it(n):
     global  balance
     balance  =  balance + n
     balance  =  balance  -  n
 
def  run_thread(n):
     for  in  range ( 100000 ):
         # 先要获取锁
         lock.acquire()
         try :
             change_it(n)
         # 一定要释放锁
         finally :
             lock.release()
 
 
t1 = threading.Thread(target = run_thread,args = ( 5 ,))
t2 = threading.Thread(target = run_thread,args = ( 8 ,))
 
t1.start()
t2.start()
t1.join()
t2.join()
print (balance)

多个线程同时执行lock.acquire()时,只有一个线程能成功地获取锁,然后继续执行代码,其他线程就继续等待直到获得锁为止。

 死锁 -- 由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁

 GIL锁 -- Global Interpreter Lock   任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行。

解决GIL锁的问题 -- 创建多进程。

 

ThreadLocal

问题的抛出:线程的全局变量和局部变量。局部变量只有属于自己的线程才能看到,全局变量的使用必须要加锁。局部变量的传递很麻烦。

解决上述问题使用 -- ThreadLocal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import  threading
 
local_school  =  threading.local()
 
def  process_student():
     std  =  local_school.student
     print ( "hello, %s (in %s)"  %  (std, threading.current_thread().name))
     
     
def  process_thread(name):
     local_school.student  =  name
     process_student()
     
     
     
t1  =  threading.Thread(target = process_thread, args = ( "alice" ,), name = 'Thread-A' )
t2  =  threading.Thread(target = process_thread,args = ( "bob" ,), name = 'Thread-B' )
 
t1.start()
t2.start()
t1.join()
t2.join()

全局变量local_school就是一个ThreadLocal对象,每个Thread对它都可以读写student属性,但互不影响。你可以把local_school看成全局变量,但每个属性如local_school.student都是线程的局部变量,可以任意读写而互不干扰,也不用管理锁的问题,ThreadLocal内部会处理。

ThreadLocal最常用的地方就是为每个线程绑定一个数据库连接,HTTP请求,用户身份信息等,这样一个线程的所有调用到的处理函数都可以非常方便地访问这些资源。


异步IO

充分利用操作系统提供的异步IO支持,就可以用单进程单线程模型来执行多任务,这种全新的模型称为事件驱动模型

Python中,单进程的异步编程模型称为协程,有了协程的支持,就可以基于事件驱动编写高效的多任务程序。


分布式进程

线程和进程优选进程,进程更稳定进程可以在各个不同的机器上,线程只能在同一个机器上。

multiprocessing模块的managers子模块支持把多进程分布到多台机器上。

一个服务进程可以作为调度者,将任务分布到其他多个进程中,依靠网络通信。


通过消息队列Queue基于managers模块,让其他机器的进程访问Queue

服务进程负责启动Queue,把Queue注册到网络上,然后往Queue里面写入任务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# task_master.py
 
import  random, time, queue
from  multiprocessing.manager  import  BaseManager
 
# 发送任务的队列:
tast_queue  =  queue.Queue()
 
# 接收结果的队列
result_queue  =  queue.Queue()
 
# 从BaseManager继承的QueueManager:
class  QueueManager(BaseManager):
     pass
     
# 把两个Queue都注册到网络上, callable参数关联了Queue对象:    
QueueManager.register( 'get_task_queue' callable = lambda :task_queue)    
QueueManager.register( 'get_result_queue' callable = lambda : result_queue)
 
# 绑定端口5000, 设置验证码'abc':
manager  =  QueueManager(address = (' ', 5000), authkey=b' abc')
 
# 启动Queue:
manager.start()
 
# 获得通过网络访问的Queue对象:
task  =  manager.get_task_queue()
result  =  manager.get_result_queue()
 
# 放几个任务进去:
for  in  range ( 10 ):
     =  random.randint( 0 10000 )
     print ( 'put task %d...'  %  n)
     task.put(n)
 
# 从result队列读取结果:
print ( 'Try get results...' )
for  in  range ( 10 ):        
     =  result.get(timeout = 10 )
     print ( 'Result: %s'  %  r)
 
# 关闭:    
manager.shutdown()
print ( 'master exit.' )

 分布式多进程环境下,添加任务到Queue不可以直接对原始的task_queue进行操作,必须通过manager.get_task_queue()获得的Queue接口添加。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# task_worker.py
 
import  time, sys, queue
from  multiprocessing.managers  import  BaseManager
 
# 创建类似的QueueManager:
class  QueueManager(BaseManager):
     pass
 
# 由于这个QueueManager只从网络上获取Queue,所以注册时只提供名字:
QueueManager.register( 'get_task_queue' )
QueueManager.register( 'get_result_queue' )
 
# 连接到服务器,也就是运行task_master.py的机器:
server_addr  =  '127.0.0.1'
print ( 'Connect to server %s...'  %  server_addr)
 
# 端口和验证码注意保持与task_master.py设置的完全一致:
=  QueueManager(address = (server_addr,  5000 ), authkey = b 'abc' )
 
# 从网络连接:
m.connect()
 
# 获取Queue的对象:
task  =  m.get_task_queue()
result  =  m.get_result_queue()
 
# 从task队列取任务,并把结果写入result队列:
for  in  range ( 10 ):    
     try :
         =  task.get(timeout = 1 )
         print ( 'run task %d * %d...'  %  (n, n))
         =  '%d * %d = %d'  %  (n, n, n * n)
         time.sleep( 1 )
         result.put(r)
     except  Queue.Empty:
         print ( 'task queue is empty.' )
 
# 处理结束:
print ( 'worker exit.' )

Queue能通过网络访问,就是通过QueueManager实现的。由于QueueManager管理的不止一个Queue,所以,要给每个Queue的网络调用接口起个名字,比如get_task_queue

authkey有什么用?这是为了保证两台机器正常通信,不被其他机器恶意干扰。如果task_worker.pyauthkeytask_master.pyauthkey不一致,肯定连接不上。

 









本文转自ting2junshui51CTO博客,原文链接: 本文转自ting2junshui51CTO博客,原文链接: ,如需转载请自行联系原作者  ,如需转载请自行联系原作者



相关文章
|
1月前
|
Java 测试技术 API
【JUC】(1)带你重新认识进程与线程!!让你深层次了解线程运行的睡眠与打断!!
JUC是什么?你可以说它就是研究Java方面的并发过程。本篇是JUC专栏的第一章!带你了解并行与并发、线程与程序、线程的启动与休眠、打断和等待!全是干货!快快快!
380 2
|
1月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
127 1
|
1月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
145 1
|
9月前
|
存储 Linux API
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
在计算机系统的底层架构中,操作系统肩负着资源管理与任务调度的重任。当我们启动各类应用程序时,其背后复杂的运作机制便悄然展开。程序,作为静态的指令集合,如何在系统中实现动态执行?本文带你一探究竟!
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
|
7月前
|
并行计算 Linux
Linux内核中的线程和进程实现详解
了解进程和线程如何工作,可以帮助我们更好地编写程序,充分利用多核CPU,实现并行计算,提高系统的响应速度和计算效能。记住,适当平衡进程和线程的使用,既要拥有独立空间的'兄弟',也需要在'家庭'中分享和并行的成员。对于这个世界,现在,你应该有一个全新的认识。
280 67
|
11月前
|
调度 开发者 Python
深入浅出操作系统:进程与线程的奥秘
在数字世界的底层,操作系统扮演着不可或缺的角色。它如同一位高效的管家,协调和控制着计算机硬件与软件资源。本文将拨开迷雾,深入探索操作系统中两个核心概念——进程与线程。我们将从它们的诞生谈起,逐步剖析它们的本质、区别以及如何影响我们日常使用的应用程序性能。通过简单的比喻,我们将理解这些看似抽象的概念,并学会如何在编程实践中高效利用进程与线程。准备好跟随我一起,揭开操作系统的神秘面纱,让我们的代码运行得更加流畅吧!
|
5月前
|
调度 开发工具 Android开发
【HarmonyOS Next】鸿蒙应用进程和线程详解
进程的定义: 进程是系统进行资源分配的基本单位,是操作系统结构的基础。 在鸿蒙系统中,一个应用下会有三类进程:
193 0
|
8月前
|
SQL 监控 网络协议
YashanDB进程线程体系
YashanDB进程线程体系
|
10月前
|
消息中间件 调度
如何区分进程、线程和协程?看这篇就够了!
本课程主要探讨操作系统中的进程、线程和协程的区别。进程是资源分配的基本单位,具有独立性和隔离性;线程是CPU调度的基本单位,轻量且共享资源,适合并发执行;协程更轻量,由程序自身调度,适合I/O密集型任务。通过学习这些概念,可以更好地理解和应用它们,以实现最优的性能和资源利用。
344 11
|
10月前
|
Java Linux 调度
硬核揭秘:线程与进程的底层原理,面试高分必备!
嘿,大家好!我是小米,29岁的技术爱好者。今天来聊聊线程和进程的区别。进程是操作系统中运行的程序实例,有独立内存空间;线程是进程内的最小执行单元,共享内存。创建进程开销大但更安全,线程轻量高效但易引发数据竞争。面试时可强调:进程是资源分配单位,线程是CPU调度单位。根据不同场景选择合适的并发模型,如高并发用线程池。希望这篇文章能帮你更好地理解并回答面试中的相关问题,祝你早日拿下心仪的offer!
234 6

热门文章

最新文章