我们都知道计算机是由硬件和软件组成的。硬件中的CPU是计算机的核心,它承担计算机的所有任务。 操作系统是运行在硬件之上的软件,是计算机的管理者,它负责资源的管理和分配、任务的调度。 程序是运行在系统上的具有某种功能的软件,比如说浏览器,音乐播放器等。 每次执行程序的时候,都会完成一定的功能,比如说浏览器帮我们打开网页,为了保证其独立性,就需要一个专门的管理和控制执行程序的数据结构——进程控制块。 进程就是一个程序在一个数据集上的一次动态执行过程。 进程一般由程序、数据集、进程控制块三部分组成。我们编写的程序用来描述进程要完成哪些功能以及如何完成;数据集则是程序在执行过程中所需要使用的资源;进程控制块用来记录进程的外部特征,描述进程的执行变化过程,系统可以利用它来控制和管理进程,它是系统感知进程存在的唯一标志。
在早期的操作系统里,计算机只有一个核心,进程执行程序的最小单位,任务调度采用时间片轮转的抢占式方式进行进程调度。每个进程都有各自的一块独立的内存,保证进程彼此间的内存地址空间的隔离。 随着计算机技术的发展,进程出现了很多弊端,一是进程的创建、撤销和切换的开销比较大,二是由于对称多处理机(对称多处理机(SymmetricalMulti-Processing)又叫SMP,是指在一个计算机上汇集了一组处理器(多CPU),各CPU之间共享内存子系统以及总线结构)的出现,可以满足多个运行单位,而多进程并行开销过大。 这个时候就引入了线程的概念。 线程也叫轻量级进程,它是一个基本的CPU执行单元,也是程序执行过程中的最小单元,由线程ID、程序计数器、寄存器集合 和堆栈共同组成。线程的引入减小了程序并发执行时的开销,提高了操作系统的并发性能。 线程没有自己的系统资源,只拥有在运行时必不可少的资源。但线程可以与同属与同一进程的其他线程共享进程所拥有的其他资源。
进程与线程之间的关系
线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除。线程可与属于同一进程的其它线程共享进程所拥有的全部资源,但是其本身基本上不拥有系统资源,只拥有一点在运行中必不可少的信息(如程序计数器、一组寄存器和栈)。
Threading用于提供线程相关的操作,线程是应用程序中工作的最小单元。
1、threading模块
threading 模块建立在 _thread 模块之上。thread 模块以低级、原始的方式来处理和控制线程,而 threading 模块通过对 thread 进行二次封装,提供了更方便的 api 来处理线程。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
import threading
import time
def worker(num):
"""
thread worker function
:return:
"""
time.sleep(1)
print("The num is %d"%num)
return
foriinrange(20):
t=threading.Thread(target=worker,args=(i,),name=“t.%d”%i)
t.start()
|
上述代码创建了20个“前台”线程,然后控制器就交给了CPU,CPU根据指定算法进行调度,分片执行指令。
Thread方法说明
t.start() : 激活线程,
t.getName() : 获取线程的名称
t.setName() : 设置线程的名称
t.name : 获取或设置线程的名称
t.is_alive() : 判断线程是否为激活状态
t.isAlive() :判断线程是否为激活状态
t.setDaemon() 设置为后台线程或前台线程(默认:False);通过一个布尔值设置线程是否为守护线程,必须在执行start()方法之后才可以使用。如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止;如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止
t.isDaemon() : 判断是否为守护线程
t.ident :获取线程的标识符。线程标识符是一个非零整数,只有在调用了start()方法之后该属性才有效,否则它只返回None。
t.join() :逐个执行每个线程,执行完毕后继续往下执行,该方法使得多线程变得无意义
t.run() :线程被cpu调度后自动执行线程对象的run方法
2、线程锁threading.RLock和threading.Lock
由于线程之间是进行随机调度,并且每个线程可能只执行n条执行之后,CPU接着执行其他线程。为了保证数据的准确性,引入了锁的概念。所以,可能出现如下问题:
例:假设列表A的所有元素就为0,当一个线程从前向后打印列表的所有元素,另外一个线程则从后向前修改列表的元素为1,那么输出的时候,列表的元素就会一部分为0,一部分为1,这就导致了数据的不一致。锁的出现解决了这个问题。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
import threading
import time
globals_num=0
lock=threading.RLock()
def Func():
lock.acquire() # 获得锁
globalglobals_num
globals_num+=1
time.sleep(1)
print(globals_num)
lock.release() # 释放锁
foriinrange(10):
t=threading.Thread(target=Func)
t.start()
|
3、threading.RLock和threading.Lock 的区别
RLock允许在同一线程中被多次acquire。而Lock却不允许这种情况。 如果使用RLock,那么acquire和release必须成对出现,即调用了n次acquire,必须调用n次的release才能真正释放所占用的琐。
|
import threading
lock=threading.Lock() #Lock对象
lock.acquire()
lock.acquire() #产生了死琐。
lock.release()
lock.release()
|
|
import threading
rLock=threading.RLock() #RLock对象
rLock.acquire()
rLock.acquire() #在同一线程内,程序不会堵塞。
rLock.release()
rLock.release()
|
4、threading.Event
python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法 set、wait、clear。
事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。
- clear:将“Flag”设置为False
- set:将“Flag”设置为True
- Event.isSet() :判断标识位是否为Ture。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
import threading
def do(event):
print('start')
event.wait()
print('execute')
event_obj=threading.Event()
foriinrange(10):
t=threading.Thread(target=do,args=(event_obj,))
t.start()
event_obj.clear()
inp=input('input:')
ifinp=='true':
event_obj.set()
|
当线程执行的时候,如果flag为False,则线程会阻塞,当flag为True的时候,线程不会阻塞。它提供了本地和远程的并发性。
5、threading.Condition
一个condition变量总是与某些类型的锁相联系,这个可以使用默认的情况或创建一个,当几个condition变量必须共享和同一个锁的时候,是很有用的。锁是conditon对象的一部分:没有必要分别跟踪。
condition变量服从上下文管理协议:with语句块封闭之前可以获取与锁的联系。 acquire() 和 release() 会调用与锁相关联的相应的方法。
其他和锁关联的方法必须被调用,wait()方法会释放锁,当另外一个线程使用 notify() or notify_all()唤醒它之前会一直阻塞。一旦被唤醒,wait()会重新获得锁并返回,
Condition类实现了一个conditon变量。 这个conditiaon变量允许一个或多个线程等待,直到他们被另一个线程通知。 如果lock参数,被给定一个非空的值,,那么他必须是一个lock或者Rlock对象,它用来做底层锁。否则,会创建一个新的Rlock对象,用来做底层锁。
- wait(timeout=None) : 等待通知,或者等到设定的超时时间。当调用这wait()方法时,如果调用它的线程没有得到锁,那么会抛出一个RuntimeError 异常。 wati()释放锁以后,在被调用相同条件的另一个进程用notify() or notify_all() 叫醒之前 会一直阻塞。wait() 还可以指定一个超时时间。
如果有等待的线程,notify()方法会唤醒一个在等待conditon变量的线程。notify_all() 则会唤醒所有在等待conditon变量的线程。
注意: notify()和notify_all()不会释放锁,也就是说,线程被唤醒后不会立刻返回他们的wait() 调用。除非线程调用notify()和notify_all()之后放弃了锁的所有权。
在典型的设计风格里,利用condition变量用锁去通许访问一些共享状态,线程在获取到它想得到的状态前,会反复调用wait()。修改状态的线程在他们状态改变时调用 notify() or notify_all(),用这种方式,线程会尽可能的获取到想要的一个等待者状态。 例子: 生产者-消费者模型,
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
|
import threading
import time
def consumer(cond):
with cond:
print("consumer before wait")
cond.wait()
print("consumer after wait")
def producer(cond):
with cond:
print("producer before notifyAll")
cond.notifyAll()
print("producer after notifyAll")
condition=threading.Condition()
c1=threading.Thread(name="c1",target=consumer,args=(condition,))
c2=threading.Thread(name="c2",target=consumer,args=(condition,))
p=threading.Thread(name="p",target=producer,args=(condition,))
c1.start()
time.sleep(2)
c2.start()
time.sleep(2)
p.start()
|
6、queue模块
Queue 就是对队列,它是线程安全的
举例来说,我们去麦当劳吃饭。饭店里面有厨师职位,前台负责把厨房做好的饭卖给顾客,顾客则去前台领取做好的饭。这里的前台就相当于我们的队列。形成管道样,厨师做好饭通过前台传送给顾客,所谓单向队列
这个模型也叫生产者-消费者模型。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
import queue
q=queue.Queue(maxsize=0) # 构造一个先进显出队列,maxsize指定队列长度,为0 时,表示队列长度无限制。
q.join() # 等到队列为kong的时候,在执行别的操作
q.qsize() # 返回队列的大小 (不可靠)
q.empty() # 当队列为空的时候,返回True 否则返回False (不可靠)
q.full() # 当队列满的时候,返回True,否则返回False (不可靠)
q.put(item,block=True,timeout=None)# 将item放入Queue尾部,item必须存在,可以参数block默认为True,表示当队列满时,会等待队列给出可用位置,
为False时为非阻塞,此时如果队列已满,会引发queue.Full异常。可选参数timeout,表示会阻塞设置的时间,过后,
如果队列无法给出放入item的位置,则引发queue.Full异常
q.get(block=True,timeout=None)# 移除并返回队列头部的一个值,可选参数block默认为True,表示获取值的时候,如果队列为空,则阻塞,为False时,不阻塞,
若此时队列为空,则引发queue.Empty异常。可选参数timeout,表示会阻塞设置的时候,过后,如果队列为空,则引发Empty异常。
q.put_nowait(item)# 等效于 put(item,block=False)
q.get_nowait()# 等效于 get(item,block=False)
|
代码如下:
方法一
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
|
#!/usr/bin/env python
import Queue
import threading
message=Queue.Queue(10)
def producer(i):
whileTrue:
message.put(i)
def consumer(i):
whileTrue:
msg=message.get()
foriinrange(12):
t=threading.Thread(target=producer,args=(i,))
t.start()
foriinrange(10):
t=threading.Thread(target=consumer,args=(i,))
t.start()
|
那就自己做个线程池吧:
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
|
# 简单往队列中传输线程数
import threading
import time
import queue
classThreadingpool():
def __init__(self,max_num=10):
self.queue=queue.Queue(max_num)
foriinrange(max_num):
self.queue.put(threading.Thread)
def getthreading(self):
returnself.queue.get()
def addthreading(self):
self.queue.put(threading.Thread)
def func(p,i):
time.sleep(1)
print(i)
p.addthreading()
if__name__=="__main__":
p=Threadingpool()
foriinrange(20):
thread=p.getthreading()
t=thread(target=func,args=(p,i))
t.start()
|
方法二
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
#往队列中无限添加任务
import queue
import threading
import contextlib
import time
StopEvent=object()
classThreadPool(object):
def __init__(self,max_num):
self.q=queue.Queue()
self.max_num=max_num
self.terminal=False
self.generate_list=[]
self.free_list=[]
def run(self,func,args,callback=None):
"""
线程池执行一个任务
:param func: 任务函数
:param args: 任务函数所需参数
:param callback: 任务执行失败或成功后执行的回调函数,回调函数有两个参数1、任务函数执行状态;2、任务函数返回值(默认为None,即:不执行回调函数)
:return: 如果线程池已经终止,则返回True否则None
"""
iflen(self.free_list)==0andlen(self.generate_list) self.max_num:
self.generate_thread()
w=(func,args,callback,)
self.q.put(w)
def generate_thread(self):
"""
创建一个线程
"""
t=threading.Thread(target=self.call)
t.start()
def call(self):
"""
循环去获取任务函数并执行任务函数
"""
current_thread=threading.currentThread
self.generate_list.append(current_thread)
event=self.q.get() # 获取线程
whileevent!=StopEvent: # 判断获取的线程数不等于全局变量
func,arguments,callback=event # 拆分元祖,获得执行函数,参数,回调函数
try:
result=func(*arguments) # 执行函数
status=True
except Exception ase: # 函数执行失败
status=False
result=e
ifcallback isnotNone:
try:
callback(status,result)
except Exception ase:
pass
# self.free_list.append(current_thread)
# event = self.q.get()
# self.free_list.remove(current_thread)
with self.work_state():
event=self.q.get()
else:
self.generate_list.remove(current_thread)
def close(self):
"""
关闭线程,给传输全局非元祖的变量来进行关闭
:return:
"""
foriinrange(len(self.generate_list)):
self.q.put(StopEvent)
def terminate(self):
"""
突然关闭线程
:return:
"""
self.terminal=True
whileself.generate_list:
self.q.put(StopEvent)
self.q.empty()
@contextlib.contextmanager
def work_state(self):
self.free_list.append(threading.currentThread)
try:
yield
finally:
self.free_list.remove(threading.currentThread)
def work(i):
print(i)
returni+1# 返回给回调函数
def callback(ret):
print(ret)
pool=ThreadPool(10)
foritem inrange(50):
pool.run(func=work,args=(item,),callback=callback)
pool.terminate()
# pool.close()
|
multiprocessing是python的多进程管理包,和threading.Thread类似。
1、multiprocessing模块
直接从侧面用subprocesses替换线程使用GIL的方式,由于这一点,multiprocessing模块可以让程序员在给定的机器上充分的利用CPU。在multiprocessing中,通过创建Process对象生成进程,然后调用它的start()方法,
|
from multiprocessing import Process
def func(name):
print('hello',name)
if__name__=="__main__":
p=Process(target=func,args=('zhangyanlin',))
p.start()
p.join() # 等待进程执行完毕
|
在使用并发设计的时候最好尽可能的避免共享数据,尤其是在使用多进程的时候。 如果你真有需要 要共享数据, multiprocessing提供了两种方式。
(1)multiprocessing,Array,Value
数据可以用Value或Array存储在一个共享内存地图里,如下:
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
|
from multiprocessing import Array,Value,Process
def func(a,b):
a.value=3.333333333333333
foriinrange(len(b)):
b[i]=-b[i]
if__name__=="__main__":
num=Value('d',0.0)
arr=Array('i',range(11))
c=Process(target=func,args=(num,arr))
d=Process(target=func,args=(num,arr))
c.start()
d.start()
c.join()
d.join()
print(num.value)
foriinarr:
print(i)
输出:
3.1415927
[0,-1,-2,-3,-4,-5,-6,-7,-8,-9]
|
创建num和arr时,“d”和“i”参数由Array模块使用的typecodes创建:“d”表示一个双精度的浮点数,“i”表示一个有符号的整数,这些共享对象将被线程安全的处理。
Array(‘i’, range(10))中的‘i’参数:
|
‘c’:ctypes.c_char ‘u’:ctypes.c_wchar ‘b’:ctypes.c_byte ‘B’:ctypes.c_ubyte
‘h’:ctypes.c_short ‘H’:ctypes.c_ushort ‘i’:ctypes.c_int ‘I’:ctypes.c_uint
‘l’:ctypes.c_long, ‘L’:ctypes.c_ulong ‘f’:ctypes.c_float ‘d’:ctypes.c_double
|
(2)multiprocessing,Manager
由Manager()返回的manager提供list, dict, Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Barrier, Queue, Value and Array类型的支持。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
from multiprocessing import Process,Manager
deff(d,l):
d["name"]="zhangyanlin"
d["age"]=18
d["Job"]="pythoner"
l.reverse()
if__name__=="__main__":
with Manager()asman:
d=man.dict()
l=man.list(range(10))
p=Process(target=f,args=(d,l))
p.start()
p.join()
print(d)
print(l)
输出:
|
|
{0.25:None,1:'1','2':2}
[9,8,7,6,5,4,3,2,1,0]
|
Server process manager比 shared memory 更灵活,因为它可以支持任意的对象类型。另外,一个单独的manager可以通过进程在网络上不同的计算机之间共享,不过他比shared memory要慢。
2、进程池(Using a pool of workers)
Pool类描述了一个工作进程池,他有几种不同的方法让任务卸载工作进程。
进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。
我们可以用Pool类创建一个进程池, 展开提交的任务给进程池。 例:
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
|
#apply
from multiprocessing import Pool
import time
def f1(i):
time.sleep(0.5)
print(i)
returni+100
if__name__=="__main__":
pool=Pool(5)
foriinrange(1,31):
pool.apply(func=f1,args=(i,))
#apply_async
def f1(i):
time.sleep(0.5)
print(i)
returni+100
def f2(arg):
print(arg)
if__name__=="__main__":
pool=Pool(5)
foriinrange(1,31):
pool.apply_async(func=f1,args=(i,),callback=f2)
pool.close()
pool.join()
|
一个进程池对象可以控制工作进程池的哪些工作可以被提交,它支持超时和回调的异步结果,有一个类似map的实现。
- processes :使用的工作进程的数量,如果processes是None那么使用 os.cpu_count()返回的数量。
- initializer: 如果initializer是None,那么每一个工作进程在开始的时候会调用initializer(*initargs)。
- maxtasksperchild:工作进程退出之前可以完成的任务数,完成后用一个心的工作进程来替代原进程,来让闲置的资源被释放。maxtasksperchild默认是None,意味着只要Pool存在工作进程就会一直存活。
- context: 用在制定工作进程启动时的上下文,一般使用 multiprocessing.Pool() 或者一个context对象的Pool()方法来创建一个池,两种方法都适当的设置了context
注意:Pool对象的方法只可以被创建pool的进程所调用。
New in version 3.2: maxtasksperchild
New in version 3.4: context
进程池的方法
- apply(func[, args[, kwds]]) :使用arg和kwds参数调用func函数,结果返回前会一直阻塞,由于这个原因,apply_async()更适合并发执行,另外,func函数仅被pool中的一个进程运行。
- apply_async(func[, args[, kwds[, callback[, error_callback]]]]) : apply()方法的一个变体,会返回一个结果对象。如果callback被指定,那么callback可以接收一个参数然后被调用,当结果准备好回调时会调用callback,调用失败时,则用error_callback替换callback。 Callbacks应被立即完成,否则处理结果的线程会被阻塞。
- close() : 阻止更多的任务提交到pool,待任务完成后,工作进程会退出。
- terminate() : 不管任务是否完成,立即停止工作进程。在对pool对象进程垃圾回收的时候,会立即调用terminate()。
- join() : wait工作线程的退出,在调用join()前,必须调用close() or terminate()。这样是因为被终止的进程需要被父进程调用wait(join等价与wait),否则进程会成为僵尸进程。
- map(func, iterable[, chunksize])¶
- map_async(func, iterable[, chunksize[, callback[, error_callback]]])¶
- imap(func, iterable[, chunksize])¶
- imap_unordered(func, iterable[, chunksize])
- starmap(func, iterable[, chunksize])¶
- starmap_async(func, iterable[, chunksize[, callback[, error_back]]])
线程和进程的操作是由程序触发系统接口,最后的执行者是系统;协程的操作则是程序员。
协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。
协程的适用场景:当程序中存在大量不需要CPU的操作时(IO),适用于协程;
event loop是协程执行的控制点, 如果你希望执行协程, 就需要用到它们。
event loop提供了如下的特性:
- 注册、执行、取消延时调用(异步函数)
- 创建用于通信的client和server协议(工具)
- 创建和别的程序通信的子进程和协议(工具)
- 把函数调用送入线程池中
协程示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
import asyncio
async def cor1():
print("COR1 start")
await cor2()
print("COR1 end")
async def cor2():
print("COR2")
loop=asyncio.get_event_loop()
loop.run_until_complete(cor1())
loop.close()
|
最后三行是重点。
- asyncio.get_event_loop() : asyncio启动默认的event loop
- run_until_complete() : 这个函数是阻塞执行的,知道所有的异步函数执行完成,
- close() : 关闭event loop。
1、greenlet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
import greenlet
def fun1():
print("12")
gr2.switch()
print("56")
gr2.switch()
def fun2():
print("34")
gr1.switch()
print("78")
gr1=greenlet.greenlet(fun1)
gr2=greenlet.greenlet(fun2)
gr1.switch()
|
2、gevent
gevent属于第三方模块需要下载安装包
- pip3 install –upgrade pip3
- pip3 install gevent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
import gevent
def fun1():
print("www.baidu.com") # 第一步
gevent.sleep(0)
print("end the baidu.com") # 第三步
def fun2():
print("www.zhihu.com") # 第二步
gevent.sleep(0)
print("end th zhihu.com") # 第四步
gevent.joinall([
gevent.spawn(fun1),
gevent.spawn(fun2),
])
|
遇到IO操作自动切换:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
import gevent
import requests
def func(url):
print("get: %s"%url)
gevent.sleep(0)
date=requests.get(url)
ret=date.text
print(url,len(ret))
gevent.joinall([
gevent.spawn(func,'https://www.python.org/'),
gevent.spawn(func,'https://www.yahoo.com/'),
gevent.spawn(func,'https://github.com/'),
])
|
工作中用到协程的地方
相关推荐
对python并发进行的笔记整理,个人所学习使用,主要包括多进程,多线程,协程,IO多路复用,进程线程通信等
Python七大类常见深度问题详解: Python类三种方法,函数传参,类与实例...Python 进程线程协程 GIL 闭包 与高阶函数(五) Python中深浅拷贝 垃圾回收与 super继承(六) Python 调度算法 死锁 静动态链接 分页分段(七)
总结起来,理解Python中的线程、进程和协程是进行并发编程的关键。生成器和装饰器提供了强大的工具,使我们能够以简洁的方式实现并发和异步操作。通过熟练掌握这些概念和技术,开发者可以编写出更加高效、灵活的...
初学者学习python代码,主要是为了自己后期查看,比较基础-并行:真的多任务 并发:假的多任务 ...协程依赖于线程,线程依赖于进程,协程切换需要资源相当少,所以效率就会很高 重构:把现在代码推倒,重新写
Python中的线程、进程和协程是并发执行任务的关键概念,它们在多任务处理和优化程序性能方面扮演着重要角色。 一、进程 进程是操作系统分配资源的基本单位,每个进程都有独立的内存空间,包括代码、数据和栈空间。...
在Python中,虽然GIL限制了多线程的并行性能,但仍然可以通过多进程或者其他并发模型如协程来利用多核处理器的优势。协程将在另一个层次上讨论,它们允许无须线程切换地实现轻量级并发,通常在IO密集型任务中表现得...
在计算机科学中,进程、线程和协程是并发执行任务的三种基本方式。进程是程序在操作系统中的实例,包含内存空间和资源;线程是进程中的执行单元,是CPU调度的基本单位,同一进程中的多个线程可以并发执行不同任务。...
在Python中,理解和掌握进程、线程、协程以及IO多路复用的概念和技术,是编写高性能网络服务和并发应用的基础。通过合理使用这些工具,开发者可以编写出更高效、更灵活的程序,适应不断增长的并发需求。在实际编程中...
在Python编程中,进程、线程和协程是并发执行任务的重要概念,它们各自有其特点和适用场景。 首先,进程是操作系统分配资源的基本单位,每个进程都有自己独立的内存空间,包括堆和栈,这意味着进程间无法直接共享...
总之,Python的`multiprocessing`模块提供了一整套工具来支持多进程编程,包括进程创建、进程池管理、进程间通信等,使得开发者能够有效地利用多核处理器的计算能力,提高程序的并发性能。同时,`subprocess`模块则...
在Python编程中,多协程、多线程和多进程是实现并发执行任务的重要机制,它们可以帮助我们提高程序的执行效率,充分利用系统资源。本文将深入探讨这些概念以及它们在Python中的实现方式。 首先,我们要了解并发的...
Python中的协程是一种轻量级的并发机制,与线程相比,协程具有更低的开销和更高的效率,尤其适用于I/O密集型任务。本文将深入探讨Python协程的定义、与线程的区别、可能遇到的问题、优势以及如何通过`yield`和`...
在Python编程中,多进程和协程是两种不同的并发处理方式,它们各有优势,并且可以协同工作以提高程序效率。本示例展示了如何在处理大量数据时,利用多进程读取文件并用协程进行数据处理,从而实现高效的数据处理流程...
python协程 线程和进程的操作是由程序触发系统接口,最后的执行者是系统;协程的操作则是程序员。 协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续...
### 解决Python线程卡死的问题 在进行Python开发的过程中,可能会遇到程序突然无响应的情况,这往往是由线程卡死引起的。线程卡死不仅会导致程序无法继续执行,还可能引发一系列连锁反应,比如资源泄露、服务中断等...
多线程对爬虫的效率提高是非凡的,当我们使用python的多线程有几点是需要我们知道的:1.Python的多线程并不如java的多线程,其差异在于当python解释器开始执行任务时,受制于GIL(全局解释所),Python的线程被限制到...
在计算机科学中,进程、线程和协程是操作系统中执行任务的基本单元,它们对于理解和优化软件性能至关重要。下面将详细解释这些概念及其相互关系。 首先,我们来谈谈"进程"。进程是操作系统中运行的一个应用程序实例...