python高级【GIL】线程,进程,进程池,线程池,进程间的通信,线程间的通信

本文介绍了Python中的全局解释器锁(GIL)、多线程与多进程的区别,强调了GIL如何影响多线程性能。在IO密集型任务中,多线程与多进程性能相近,而在CPU密集型任务中,Python的多线程受限于GIL。此外,文章还探讨了线程间的通信、同步机制如加锁、死锁、Condition和Semaphore,并提到了线程池的实现及进程池的使用,以及不同类型的队列在进程间通信的角色。
摘要由CSDN通过智能技术生成

1.概念

#gil global interpreter lock (cpython)
#python中一个线程对应于c语言中的一个线程
#gil使得同一个时刻只有一个线程在一个cpu上执行字节码, 无法将多个线程映射到多个cpu上执行(而C和java是可以的)
#有了GIL是不是就是代表线程是绝对安全的? 不是
#gil会根据执行的字节码行数以及时间片释放gil,gil在遇到io的操作时候主动释放

#gil global interpreter lock (cpython)
#python中一个线程对应于c语言中的一个线程
#gil使得同一个时刻只有一个线程在一个cpu上执行字节码, 无法将多个线程映射到多个cpu上执行(而C和java是可以的)
#有了GIL是不是就是代表线程是绝对安全的? 不是
#gil会根据执行的字节码行数以及时间片释放gil,gil在遇到io的操作时候主动释放
# import dis
# def add(a):
#     a = a+1
#     return a
# 
# print(dis.dis(add)) #反编译

total = 0

def add():
    #1. dosomething1
    #2. io操作
    # 1. dosomething3
    global total
    for i in range(1000000):
        total += 1
def desc():
    global total
    for i in range(1000000):
        total -= 1

import threading
thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=desc)
thread1.start()
thread2.start()

thread1.join()
thread2.join()
print(total)

-314650

2.多线程

#对于io操作来说,多线程和多进程性能差别不大

爬取网页是一个socket的过程,socket属于io编程

虽然有GIL的存在,同一时刻只能有一个线程在一个cpu上执行,但是python的内部的调度的原因,会在以字节码行数或时间片的情况下,IO操作的情况下,对线程进行切换(释放GIL)。相当于多个线程同时在运行。

#对于非io操作来说

而对于不是IO的操作,python中的多线程是通过时间片的切换来实现,虽然多个线程可以在多个cpu上执行,但同一时刻只能有一个线程在一个cpu上执行。而对于c和java来说,可以将多个线程同时映射到多个cpu上。这样python的速度就有所下降。

#对于io操作来说,多线程和多进程性能差别不大
#1.通过Thread类实例化
#thread1.setDaemon(True)  #守护线程
#thread1.join()  #等待线程执行完毕在退出


import time
import threading

def get_detail_html(url):
    print("get detail html started")
    time.sleep(2)
    print("get detail html end")


def get_detail_url(url):
    print("get detail url started")
    time.sleep(4)
    print("get detail url end")


#2. 通过集成Thread来实现多线程


class GetDetailHtml(threading.Thread):
    def __init__(self, name):
        super().__init__(name=name)

    def run(self):
        print("get detail html started")
        time.sleep(2)
        print("get detail html end")


class GetDetailUrl(threading.Thread):
    def __init__(self, name):
        super().__init__(name=name)

    def run(self):
        print("get detail url started")
        time.sleep(4)
        print("get detail url end")

if  __name__ == "__main__":
    thread1 = GetDetailHtml("get_detail_html")
    thread2 = GetDetailUrl("get_detail_url")
    start_time = time.time()
    thread1.start()
    thread2.start()

    thread1.join()
    thread2.join()

    #当主线程退出的时候, 子线程kill掉
    print ("last time: {}".format(time.time()-start_time))

3.线程间的通信

1.通过共享变量

2.通过queue

4.线程间的同步(加锁)

1.使用锁会影响程序的性能,锁需要释放

2.为了提高性能,引入了不同的锁

5.死锁

1)情况1(从同一个锁的角度讲)

lock.acquire()
lock.acquire()

当获得锁之后,如果不release,就会一直阻塞;上面的代码中,在第一行多的锁之后,我们又在第二行加了一把锁,由于第一把锁已经加上,别的线程不能再执行,无法释放锁,而这个线程又在下一行加锁,就会一直阻塞

2.从资源获取的角度讲(多个锁)

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

import threading
import time
 
mutexA = threading.Lock()
mutexB = threading.Lock()
 
class MyThread(threading.Thread):
 
    def __init__(self):
        threading.Thread.__init__(self)
 
    def run(self):
        self.fun1()
        self.fun2()
 
    def fun1(self):
 
        mutexA.acquire()  # 如果锁被占用,则阻塞在这里,等待锁的释放
 
        print ("I am %s , get res: %s---%s" %(self.name, "ResA",time.time()))
 
        mutexB.acquire()
        print ("I am %s , get res: %s---%s" %(self.name, "ResB",time.time()))
        mutexB.release()
        mutexA.release()
 
 
    def fun2(self):
 
        mutexB.acquire()
        print ("I am %s , get res: %s---%s" %(self.name, "ResB",time.time()))
        time.sleep(0.2)
 
        mutexA.acquire()
        print ("I am %s , get res: %s---%s" %(self.name, "ResA",time.time()))
        mutexA.release()
 
        mutexB.release()
 
if __name__ == "__main__":
 
    print("start---------------------------%s"%time.time())
 
    for i in range(0, 10):
        my_thread = MyThread()
        my_thread.start()

出现死锁不停竞争,程序卡住。

6.Condition的用法【线程间的同步】

 

import threading

#条件变量, 用于复杂的线程间同步
# class XiaoAi(threading.Thread):
#     def __init__(self, lock):
#         super().__init__(name="小爱")
#         self.lock = lock
#
#     def run(self):
#         self.lock.acquire()
#         print("{} : 在 ".format(self.name))
#         self.lock.release()
#
#         self.lock.acquire()
#         print("{} : 好啊 ".format(self.name))
#         self.lock.release()
#
# class TianMao(threading.Thread):
#     def __init__(self, lock):
#         super().__init__(name="天猫精灵")
#         self.lock = lock
#
#     def run(self):
#
#         self.lock.acquire()
#         print("{} : 小爱同学 ".format(self.name))
#         self.lock.release()
#
#         self.lock.acquire()
#         print("{} : 我们来对古诗吧 ".format(self.name))
#         self.lock.release()

#通过condition完成协同读诗

class XiaoAi(threading.Thread):
    def __init__(self, cond):
        super().__init__(name="小爱")
        self.cond = cond

    def run(self):
        with self.cond:
            self.cond.wait()
            print("{} : 在 ".format(self.name))
            self.cond.notify()

            self.cond.wait()
            print("{} : 好啊 ".format(self.name))
            self.cond.notify()

            self.cond.wait()
            print("{} : 君住长江尾 ".format(self.name))
            self.cond.notify()

            self.cond.wait()
            print("{} : 共饮长江水 ".format(self.name))
            self.cond.notify()

            self.cond.wait()
            print("{} : 此恨何时已 ".format(self.name))
            self.cond.notify()

            self.cond.wait()
            print("{} : 定不负相思意 ".format(self.name))
            self.cond.notify()

class TianMao(threading.Thread):
    def __init__(self, cond):
        super().__init__(name="天猫精灵")
        self.cond = cond

    def run(self):
        with self.cond:
            print("{} : 小爱同学 ".format(self.name))
            self.cond.notify()  #通知wait()的线程方法的启动,前提是先有wait()等待
            self.cond.wait()

            print("{} : 我们来对古诗吧 ".format(self.name))
            self.cond.notify()
            self.cond.wait()

            print("{} : 我住长江头 ".format(self.name))
            self.cond.notify()
            self.cond.wait()

            print("{} : 日日思君不见君 ".format(self.name))
            self.cond.notify()
            self.cond.wait()

            print("{} : 此水几时休 ".format(self.name))
            self.cond.notify()
            self.cond.wait()

            print("{} : 只愿君心似我心 ".format(self.name))
            self.cond.notify()
            self.cond.wait()



if __name__ == "__main__":
    from concurrent import futures
    cond = threading.Condition()
    xiaoai = XiaoAi(cond)
    tianmao = TianMao(cond)

    #启动顺序很重要
    #在调用with cond之后才能调用wait或者notify方法
    #condition有两层锁, 一把底层锁会在线程调用了wait方法的时候释放, 上面的锁会在每次调用wait的时候分配一把并放入到cond的等待队列中,等到notify方法的唤醒
    xiaoai.start()
    tianmao.start()

7.#Semaphore 是用于控制进入数量的锁【线程间的同步】

#Semaphore 是用于控制进入数量的锁
#文件, 读、写, 写一般只是用于一个线程写,读可以允许有多个

#做爬虫
import threading
import time

class HtmlSpider(threading.Thread):
    def __init__(self, url, sem):
        super().__init__()
        self.url = url
        self.sem = sem

    def run(self):
        time.sleep(2)
        print("got html text success")
        self.sem.release()                         #消费 加一

class UrlProducer(threading.Thread):
    def __init__(self, sem):
        super().__init__()
        self.sem = sem

    def run(self):
        for i in range(20):
            self.sem.acquire() #产生 减一   为零就阻塞
            html_thread = HtmlSpider("https://baidu.com/{}".format(i), self.sem)
            html_thread.start()

if __name__ == "__main__":
    sem = threading.Semaphore(3)
    url_producer = UrlProducer(sem)
    url_producer.start()

8.线程池的实现方式   ThreadPoolExecutor

#实现线程池的方式一
from concurrent.futures import ThreadPoolExecutor, as_completed, wait, FIRST_COMPLETED
from concurrent.futures import Future
from multiprocessing import Pool

#未来对象,task的返回容器


#线程池, 为什么要线程池【线程池实现程序的并发】,虽然用Condition也能实现,但线程池提供更多的功能
#主线程中可以获取某一个线程的状态或者某一个任务的状态,以及返回值
#当一个线程完成的时候我们主线程能立即知道
#futures可以让多线程和多进程编码接口一致
import time

def get_html(times):
    time.sleep(times)
    print("get page {} success".format(times))
    return times



executor = ThreadPoolExecutor(max_workers=2)
#通过submit函数提交执行的函数到线程池中, submit 是立即返回
# task1 = executor.submit(get_html, (3))
# task2 = executor.submit(get_html, (2))


#要获取已经成功的task的返回
urls = [3,2,4]
all_task = [executor.submit(get_html, (url)) for url in urls]
wait(all_task, return_when=FIRST_COMPLETED)#加入自己的执行逻辑,第一个url中的任务执行完毕才往下执行
print("main")
# for future in as_completed(all_task):#这个是谁先执行完毕谁就输出结果,as_completed()方法用来取结果
#     data = future.result()
#     print("get {} page".format(data))

#通过executor的map获取已经完成的task的值,map返回的结果的顺序和url中的顺序一样
# for data in executor.map(get_html, urls):
#     print("get {} page".format(data))


# #done方法用于判定某个任务是否完成
# print(task1.done())
# print(task2.cancel())
# time.sleep(3)
# print(task1.done())
#
# #result方法可以获取task的执行结果
# print(task1.result())

9.多进程与多线程的比较

https://www.cnblogs.com/haiyan123/p/7461294.html

import time
from concurrent.futures import ThreadPoolExecutor, as_completed  #线程池
from concurrent.futures import ProcessPoolExecutor   #进程池
#多进程编程
#耗cpu的操作,用多进程编程, 对于io操作来说, 使用多线程编程,进程切换代价要高于线程

#1. 对于耗费cpu的操作,多进程由于多线程
# def fib(n):
#     if n<=2:
#         return 1
#     return fib(n-1)+fib(n-2)
#
# if __name__ == "__main__":
#     with ThreadPoolExecutor(3) as executor:
#         all_task = [executor.submit(fib, (num)) for num in range(25,40)]
#         start_time = time.time()
#         for future in as_completed(all_task):
#             data = future.result()
#             print("exe result: {}".format(data))
#
#         print("last time is: {}".format(time.time()-start_time))

#2. 对于io操作来说,多线程优于多进程
def random_sleep(n):
    time.sleep(n)
    return n
#在多线程和多进程的执行下,在windows系统中,必须在__name__=='__main__'下进行,不然会报错,linux下不用
if __name__ == "__main__":
    with ProcessPoolExecutor(3) as executor:
        all_task = [executor.submit(random_sleep, (num)) for num in [2]*30]
        start_time = time.time()
        for future in as_completed(all_task):
            data = future.result()
            print("exe result: {}".format(data))

        print("last time is: {}".format(time.time()-start_time))

10.multiprocessing

# progress = multiprocessing.Process(target=get_html, args=(2,))  #实现多进程

#实现线程池       pool = multiprocessing.Pool(multiprocessing.cpu_count())

由于GIL的存在,Python不存在多线程,要充分利用多核资源,就需要使用多进程。

multiprocessing模块是Python中的多进程管理包。

通过multiprocessing.Process对象来创建一个进程,Process对象与Thread对象的用法相同,也有start(), run(), join()的方法。

multiprocessing与threading一样,调用同一套API。

创建多进程也可以通过继承multiprocessing.Process 的类实现run()方法来来实现,和Thread()的形式一样

# import os
# #fork只能用于linux/unix中
# pid = os.fork()  #会在linux上创建一个子进程,存在两个进程,会在执行两次代码
# print("bobby")
# if pid == 0:
#   print('子进程 {} ,父进程是: {}.' .format(os.getpid(), os.getppid()))
# else:
#   print('我是父进程:{}.'.format(pid))


import multiprocessing  #更加底层的实现多进程

#多进程编程
import time
def get_html(n):
    time.sleep(n)
    print("sub_progress success")
    return n


if __name__ == "__main__":
    # progress = multiprocessing.Process(target=get_html, args=(2,))  #实现多进程
    # print(progress.pid)
    # progress.start()
    # print(progress.pid)
    # progress.join()
    # print("main progress end")

    #使用线程池
    pool = multiprocessing.Pool(multiprocessing.cpu_count())
    # result = pool.apply_async(get_html, args=(3,))  #异步添加任务
    #
    # #等待所有任务完成
    # pool.close()
    # pool.join()
    #
    # print(result.get())

    #imap
    # for result in pool.imap(get_html, [1,5,3]):  #结果与列表中的顺序一样
    #     print("{} sleep success".format(result))

    for result in pool.imap_unordered(get_html, [1,5,3]): #谁先执行完,谁先输出结果
        print("{} sleep success".format(result))




11.三个queue

from queue import Queue     线程间的通信,不适用与进程间

from multiprocessing import Process, Queue,  Pool  进程间的通信,不适用于进程池(Pool)

from multiprocessing import Process,Manager     (Manager创建的实例的Queue)   适用于进程池的Queue通信

进程间的通信不能通过共享变量来实现,可以通过Manager实现共享变量的进程间的通信。

进程间的通信还包括Pipe

import time
from multiprocessing import Process, Queue, Pool, Manager, Pipe


# def producer(queue):
#     queue.put("a")
#     time.sleep(2)
#
# def consumer(queue):
#     time.sleep(2)
#     data = queue.get()
#     print(data)
#
# if __name__ == "__main__":
#     queue = Queue(10)
#     my_producer = Process(target=producer, args=(queue,))
#     my_consumer = Process(target=consumer, args=(queue,))
#     my_producer.start()
#     my_consumer.start()
#     my_producer.join()
#     my_consumer.join()

#共享全局变量通信
#共享全局变量不能适用于多进程编程,可以适用于多线程


# def producer(a):
#     a += 100
#     time.sleep(2)
#
# def consumer(a):
#     time.sleep(2)
#     print(a)
#
# if __name__ == "__main__":
#     a = 1
#     my_producer = Process(target=producer, args=(a,))
#     my_consumer = Process(target=consumer, args=(a,))
#     my_producer.start()
#     my_consumer.start()
#     my_producer.join()
#     my_consumer.join()

#multiprocessing中的queue不能用于pool进程池
#pool中的进程间通信需要使用manager中的queue

# def producer(queue):
#     queue.put("a")
#     time.sleep(2)
#
# def consumer(queue):
#     time.sleep(2)
#     data = queue.get()
#     print(data)
#
# if __name__ == "__main__":
#     queue = Manager().Queue(10)
#     pool = Pool(2)
#
#     pool.apply_async(producer, args=(queue,))
#     pool.apply_async(consumer, args=(queue,))
#
#     pool.close()
#     pool.join()

#通过pipe实现进程间通信
#pipe的性能高于queue

# def producer(pipe):
#     pipe.send("bobby")
#
# def consumer(pipe):
#     print(pipe.recv())
#
# if __name__ == "__main__":
#     recevie_pipe, send_pipe = Pipe()
#     #pipe只能适用于两个进程
#     my_producer= Process(target=producer, args=(send_pipe, ))
#     my_consumer = Process(target=consumer, args=(recevie_pipe,))
#
#     my_producer.start()
#     my_consumer.start()
#     my_producer.join()
#     my_consumer.join()

def add_data(p_dict, key, value):
    p_dict[key] = value

if __name__ == "__main__":
    progress_dict = Manager().dict()
    from queue import PriorityQueue

    first_progress = Process(target=add_data, args=(progress_dict, "bobby1", 22))
    second_progress = Process(target=add_data, args=(progress_dict, "bobby2", 23))

    first_progress.start()
    second_progress.start()
    first_progress.join()
    second_progress.join()

    print(progress_dict)

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值