Python开发之路(十)

二、并发编程之——线程

    什么是线程?每一个进程中,都至少有一个线程。就像是我们上课听老师讲课,不仅耳朵要听,脑袋还要跟着思考。如果一个程序在运行时,只有进程,那么如果遇到阻塞,程序就运行不下去了。那么如何开启线程呢?上代码:

import time
from threading import Thread
def func(i):
    time.sleep(1)

Thread(target=func,args=(1,)).start()


1.线程的特点

    1)每个进程里至少有一个主线程负责执行代码。

    2)在主线程中可以再开启一个新的线程

    3)在同一个进程中就有两个线程同时在工作

    4)线程才是CPU调度的最小单位

    5)多个线程之间的数据是共享的

    6)使用多线程处理高计算型场景 Python并不占有事

    7)在用一个进程中,同一时刻,只能有一个线程访问CPU

注:Python代码的执行由Python虚拟机(也叫解释器主循环)来控制。Python在设计之初就考虑到要在主循环中,同时只有一个线程在执行。虽然Python 解释器中可以运行多个线程,但在任意时刻只有一个线程在解释器中运行。Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。


2.守护线程

什么是守护线程?同进程的道理一样,主线程结束了之后守护线程也同时结束,守护线程会等待主线程完全结束之后才结束。


3.锁

 1)死锁

    什么是死锁?是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,他们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。下面代码就会产生死锁现象。

from threading import Lock as Lock
import time
mutexA=Lock()
mutexA.acquire()
mutexA.acquire()
print(123)
mutexA.release()
mutexA.release()

解决办法,递归锁,在Python中为了支持在同一线程中多次请求同一资源,Python提供了可重入锁RLock。这个RLock内部维护者一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个县城所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,就不会出现死锁。即:

from threading import RLock as Lock
import time
mutexA=Lock()
mutexA.acquire()
mutexA.acquire()
print(123)
mutexA.release()
mutexA.release()


4.信号量

同进程的一样,Semaphore管理一个内置的计数器,例如同时只有5个线程可以获得semaphore,即可以限制最大连接数是5.


5.事件

同进程的一样

event.isSet():返回event的状态值;

event.wait():如果 event.isSet()==False将阻塞线程;

event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态,等待操作系统调度;

event.clear():恢复event的状态值为False。


6.线程池

同进程池一样,假如我们忙时需要跑成百上千万个任务,闲时可能只有零零星星个任务。那么在成百上千万个任务需要被执行时,我们就需要去创建上千万个进程/线程吗?首先,无论创建进程还是线程都是有时间消费的,销毁进程/线程也是有时间消费的,并且,即使创建出了成百上千万个进程,操作系统也不能让他们同时执行,这样反而会影响程序的效率。因此我们不能无限制的根据任务开启或结束进程/线程。下面是一个简单的池子:

import time
from concurrent.futures import ThreadPoolExecutor
def func(num):
    time.sleep(1)
    print(num)

t= ThreadPoolExecutor(20)
for i in range(100):
    t.submit(func,i)
t.shutdown()          # join了整个池子

这个池子中放了20个线程,随用随取,用完放回。这样就大大提高了我们的执行效率。

实例:模拟多线程网页爬取

import time
import random
from concurrent.futures import ThreadPoolExecutor

urls = [
    'www.baidu.com',
    'www.sogou.com',
    'www.sohu.com',
    'www.sina.com',
    'www.163.com',
    'www.python.org',

]

def analies(content):
    print('分析网页')
    print(content.result())

def get_url(url):
    time.sleep(random.uniform(1,3))
    return url*10

t = ThreadPoolExecutor(3)
for url in urls:
    t.submit(get_url,url).add_done_callback(analies)


你可能感兴趣的:(Python开发之路)