Python &进程与线程

之前在杭州一家互联网公司任职网络爬虫,用的还是PHP,现在主流的爬虫语言应当非Python莫属了,赶紧学起来!

进程就是操作系统中执行的一个程序,操作系统以进程为单位分配存储空间,每个进程都有自己的地址空间、数据栈以及其他用于跟踪进程执行的辅助数据,操作系统管理所有进程的执行,为它们合理的分配资源。进程可以通过forkspawn的方式来创建新的进程来执行其他的任务,不过新的进程也有自己独立的内存空间,因此必须通过进程间通信机制(IPC,Inter-Process Communication)来实现数据共享,具体的方式包括管道、信号、套接字、共享内存区等。
一个进程还可以拥有多个并发的执行线索,简单的说就是拥有多个可以获得CPU调度的执行单元,这就是所谓的线程。由于线程在同一个进程下,它们可以共享相同的上下文,因此相对于进程而言,线程间的信息共享和通信更加容易。当然在单核CPU系统中,真正的并发是不可能的,因为在某个时刻能够获得CPU的只有唯一的一个线程,多个线程共享了CPU的执行时间。使用多线程实现并发编程为程序带来的好处是不言而喻的,最主要的体现在提升程序的性能和改善用户体验。Python既支持多进程又支持多线程,因此使用Python实现并发编程主要有3种方式多进程、多线程、多进程+多线程
先放张图叭,展示下多线程和多进程的优越性

1. Python多进程(Multiprocessing)

Python的OS模块封装了常见的系统调用,比如fork()函数,进程可以利用它在Python中轻松的创建子进程。子进程是父进程的拷贝,但是子进程拥有自己的进程识别号PID(Process Identification),父进程中可以通过fork()函数的返回值得到子进程的PID,而子进程中的返回值永远都是0。子进程可以通过getpid()函数获取父进程的PID。一个父进程可以fork出很多子进程。

由于Windows系统没有fork()调用,因此要实现跨平台的多进程编程,可以使用multiprocessing模块的Process类来创建子进程,而且该模块还提供了更高级的封装,例如批量启动进程的进程池(Pool)、用于进程间通信的队列(Queue)管道(Pipe)等。

在这里插入图片描述

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')`

如果要启动大量的子进程,可以采用进程池(Pool)的方式来创建子进程:
Pool常用的方法:

方法含义
apply()同步执行(串行)
apply_async()异步执行(并行)
terminate()立刻关闭进程池
join()主进程等待所有子进程执行完毕。必须在close或terminate()之后使用
close()等待所有进程结束后,才关闭进程池
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 runs %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 subprocess done....')
    p.close()
    p.join()
    print('All subprocess done')

在这里插入图片描述

在Unix/Linux下,可以使用fork()调用实现多进程。

要实现跨平台的多进程,可以使用multiprocessing模块。

进程间通信是通过Queue、Pipes等实现的。

参考资料:https://www.liaoxuefeng.com/wiki/1016959663602400/1017628290184064,感谢廖老师网站上通俗易懂的讲解!

Python多线程

Python中使用线程有两种方式:函数或者用类来包装线程对象
函数式:调用 _thread 模块中的start_new_thread()函数来产生新线程。

Python3 通过两个标准库_threadthreading 提供对线程的支持。
_thread 提供了低级别的、原始的线程以及一个简单的锁,它相比于 threading 模块的功能还是比较有限的。 threading 模块除了包含 _thread 模块中的所有方法外,还提供的其他方法:

方法名解释
threading.currentThread()返回当前的线程变量
threading.enumerate()返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程
threading.activeCount()返回正在运行的线程数量,与len(threading.enumerate())有相同的结果

类式: THread类提供以下方法来处理线程:

方法解释
un()用以表示线程活动的方法
start()启动线程活动
join([time])等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生
isAlive()返回线程是否活动的
getName()返回线程名
setName()设置线程名
#-*-coding:gbk-*-
import  threading
import time
exitFlag = 0
class myThread(threading.Thread):
    def __init__(self,threadID,name,counter):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.counter = counter
    def run(self):
        print('开始线程:'+self.name)
        print_time(self.name,self.counter,5)
        print("退出线程:"+self.name)
def print_time(threadName,delay,counter):
    while  counter:
        if exitFlag:
            threadName.exit()
        time.sleep(delay)
        print("%s:%s"%(threadName,time.ctime(time.time())))
        counter-=1
        
thread1 = myThread(1,"Thread-1",1)
thread2 = myThread(2,"Thread-2",2)

if __name__ == '__main__':
    thread1.start()
    thread2.start()
    thread1.join()
    thread2.join()
    print_time('退出主线程')

参考网站:https://www.runoob.com/python3/python3-multithreading.html

多线程和多进程最大的不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了。在这种情况下,“锁”(Lock)就可以派上用场了。我们可以通过“锁”来保护“临界资源”,只有获得“锁”的线程才能访问“临界资源”,而其他没有得到“锁”的线程只能被阻塞起来,直到获得“锁”的线程释放了“锁”,其他线程才有机会获得“锁”,进而访问被保护的“临界资源”。

from time import sleep
from threading import Thread, Lock


class Account(object):

    def __init__(self):
        self._balance = 0
        self._lock = Lock()

    def deposit(self, money):
        # 先获取锁才能执行后续的代码
        self._lock.acquire()
        try:
            new_balance = self._balance + money
            sleep(0.01)
            self._balance = new_balance
        finally:
            # 在finally中执行释放锁的操作保证正常异常锁都能释放
            self._lock.release()

    @property
    def balance(self):
        return self._balance


class AddMoneyThread(Thread):

    def __init__(self, account, money):
        super().__init__()
        self._account = account
        self._money = money

    def run(self):
        self._account.deposit(self._money)


def main():
    account = Account()
    threads = []
    for _ in range(100):
        t = AddMoneyThread(account, 1)
        threads.append(t)
        t.start()
    for t in threads:
        t.join()
    print('账户余额为: ¥%d元' % account.balance)


if __name__ == '__main__':
    main()

获得锁的线程用完后一定要释放锁,否则那些苦苦等待锁的线程将永远等待下去,成为死线程。所以我们用try...finally来确保锁一定会被释放。

新概念
协程单线程的异步编程模型,有了协程的支持,就可以基于事件驱动编写高效的多任务程序。

1、资源项目源码均已通过严格测试验证,保证能够正常运行;、 2项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行;、 2项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值