最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python中協(xié)程用法代碼詳解

 更新時(shí)間:2018年02月10日 10:47:39   作者:ITxiaoke  
這篇文章主要介紹了Python中協(xié)程用法代碼詳解,分享了相關(guān)代碼示例,小編覺得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下

本文研究的主要是python中協(xié)程的相關(guān)問題,具體介紹如下。

Num01–>協(xié)程的定義

協(xié)程,又稱微線程,纖程。英文名Coroutine。

首先我們得知道協(xié)程是啥?協(xié)程其實(shí)可以認(rèn)為是比線程更小的執(zhí)行單元。 為啥說他是一個(gè)執(zhí)行單元,因?yàn)樗詭PU上下文。這樣只要在合適的時(shí)機(jī), 我們可以把一個(gè)協(xié)程 切換到另一個(gè)協(xié)程。 只要這個(gè)過程中保存或恢復(fù) CPU上下文那么程序還是可以運(yùn)行的。

Num02–>協(xié)程和線程的差異

那么這個(gè)過程看起來和線程差不多。其實(shí)不然, 線程切換從系統(tǒng)層面遠(yuǎn)不止保存和恢復(fù) CPU上下文這么簡單。 操作系統(tǒng)為了程序運(yùn)行的高效性每個(gè)線程都有自己緩存Cache等等數(shù)據(jù),操作系統(tǒng)還會(huì)幫你做這些數(shù)據(jù)的恢復(fù)操作。 所以線程的切換非常耗性能。但是協(xié)程的切換只是單純的操作CPU的上下文,所以一秒鐘切換個(gè)上百萬次系統(tǒng)都抗的住。

Num03–>協(xié)程帶來的問題

協(xié)程有一個(gè)問題,就是系統(tǒng)并不感知,所以操作系統(tǒng)不會(huì)幫你做切換。 那么誰來幫你做切換?讓需要執(zhí)行的協(xié)程更多的獲得CPU時(shí)間才是問題的關(guān)鍵。

舉個(gè)例子如下:

目前的協(xié)程框架一般都是設(shè)計(jì)成 1:N 模式。所謂 1:N 就是一個(gè)線程作為一個(gè)容器里面放置多個(gè)協(xié)程。 那么誰來適時(shí)的切換這些協(xié)程?答案是有協(xié)程自己主動(dòng)讓出CPU,也就是每個(gè)協(xié)程池里面有一個(gè)調(diào)度器, 這個(gè)調(diào)度器是被動(dòng)調(diào)度的。意思就是他不會(huì)主動(dòng)調(diào)度。而且當(dāng)一個(gè)協(xié)程發(fā)現(xiàn)自己執(zhí)行不下去了(比如異步等待網(wǎng)絡(luò)的數(shù)據(jù)回來,但是當(dāng)前還沒有數(shù)據(jù)到), 這個(gè)時(shí)候就可以由這個(gè)協(xié)程通知調(diào)度器,這個(gè)時(shí)候執(zhí)行到調(diào)度器的代碼,調(diào)度器根據(jù)事先設(shè)計(jì)好的調(diào)度算法找到當(dāng)前最需要CPU的協(xié)程。 切換這個(gè)協(xié)程的CPU上下文把CPU的運(yùn)行權(quán)交個(gè)這個(gè)協(xié)程,直到這個(gè)協(xié)程出現(xiàn)執(zhí)行不下去需要等等的情況,或者它調(diào)用主動(dòng)讓出CPU的API之類,觸發(fā)下一次調(diào)度。

Num04–>協(xié)程的好處

在IO密集型的程序中由于IO操作遠(yuǎn)遠(yuǎn)慢于CPU的操作,所以往往需要CPU去等IO操作。 同步IO下系統(tǒng)需要切換線程,讓操作系統(tǒng)可以在IO過程中執(zhí)行其他的東西。 這樣雖然代碼是符合人類的思維習(xí)慣但是由于大量的線程切換帶來了大量的性能的浪費(fèi),尤其是IO密集型的程序。

所以人們發(fā)明了異步IO。就是當(dāng)數(shù)據(jù)到達(dá)的時(shí)候觸發(fā)我的回調(diào)。來減少線程切換帶來性能損失。 但是這樣的壞處也是很大的,主要的壞處就是操作被 “分片” 了,代碼寫的不是 “一氣呵成” 這種。 而是每次來段數(shù)據(jù)就要判斷 數(shù)據(jù)夠不夠處理哇,夠處理就處理吧,不夠處理就在等等吧。這樣代碼的可讀性很低,其實(shí)也不符合人類的習(xí)慣。

但是協(xié)程可以很好解決這個(gè)問題。比如 把一個(gè)IO操作 寫成一個(gè)協(xié)程。當(dāng)觸發(fā)IO操作的時(shí)候就自動(dòng)讓出CPU給其他協(xié)程。要知道協(xié)程的切換很輕的。 協(xié)程通過這種對異步IO的封裝 既保留了性能也保證了代碼的容易編寫和可讀性。在高IO密集型的程序下很好。但是高CPU密集型的程序下沒啥好處。

Num05–>yield實(shí)現(xiàn)一個(gè)簡單協(xié)程案例

import time
def A():
  while True:
    print("----我是A函數(shù)---")
    yield
    time.sleep(0.5)
def B(c):
  while True:
    print("----我是B函數(shù)---")
    next(c)
    time.sleep(0.5)
if __name__ == '__main__':
  a = A()
  B(a)

# 結(jié)果如下:
# ----我是B函數(shù)---
# ----我是A函數(shù)---
# ----我是B函數(shù)---
# ----我是A函數(shù)---
# ----我是B函數(shù)---
# ----我是A函數(shù)---
# ----我是B函數(shù)---
# ----我是A函數(shù)---
# ----我是B函數(shù)---
# ----我是A函數(shù)---
# ......

Num06–>greenlet版本實(shí)現(xiàn)協(xié)程案例

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author : xiaoke
from greenlet import greenlet
import time


def test1():
  while True:
    print("---我是A函數(shù)--")
    gr2.switch()
    time.sleep(0.5)


def test2():
  while True:
    print("---我是B函數(shù)--")
    gr1.switch()
    time.sleep(0.5)


def main():
  # 切換到gr1中運(yùn)行
  gr1.switch()


if __name__ == '__main__':
  gr1 = greenlet(test1)
  gr2 = greenlet(test2)
  main()

# 結(jié)果如下:
# ---我是A函數(shù)--
# ---我是B函數(shù)--
# ---我是A函數(shù)--
# ---我是B函數(shù)--
# ---我是A函數(shù)--
# ---我是B函數(shù)--
# ---我是A函數(shù)--
# ---我是B函數(shù)--
# ......

Num07–>gevent實(shí)現(xiàn)協(xié)程案例

原理:其原理是當(dāng)一個(gè)greenlet遇到IO(指的是input output 輸入輸出,比如網(wǎng)絡(luò)、文件操作等)操作時(shí),比如訪問網(wǎng)絡(luò),就自動(dòng)切換到其他的greenlet,等到IO操作完成,再在適當(dāng)?shù)臅r(shí)候切換回來繼續(xù)執(zhí)行。

由于IO操作非常耗時(shí),經(jīng)常使程序處于等待狀態(tài),有了gevent為我們自動(dòng)切換協(xié)程,就保證總有g(shù)reenlet在運(yùn)行,而不是等待IO

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author : xiaoke
import gevent


def task1(n):
  for i in range(n):
    print('----task1-----')

    gevent.sleep(1)
    # time.sleep(1) # time.sleep沒有讓gevent感知到等待


def task2(n):
  for i in range(n):
    print('----task2-----')

    gevent.sleep(1)
    # time.sleep(1)


def main():
  g1 = gevent.spawn(task1, 5)
  g2 = gevent.spawn(task2, 5)

  g1.join()
  g2.join()


if __name__ == "__main__":
  main()


# 結(jié)果如下:
# ----task1-----
# ----task2-----
# ----task1-----
# ----task2-----
# ----task1-----
# ----task2-----
# ----task1-----
# ----task2-----
# ----task1-----
# ----task2-----

gevent并發(fā)下載器

實(shí)際代碼里,我們不會(huì)用gevent.sleep()去切換協(xié)程,而是在執(zhí)行到IO操作時(shí),gevent自動(dòng)切換,代碼如下

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author : xiaoke

import urllib.request # py3

import gevent
from gevent import monkey
# 猴子補(bǔ)丁,將標(biāo)準(zhǔn)庫的涉及IO操作方法替換成gevent
monkey.patch_all() 

# 協(xié)程的任務(wù)函數(shù)
def my_download(url):
  print('GET %s' % url)

  response = urllib.request.urlopen(url)
  data = response.read()

  print('下載 %d bytes from %s' % (len(data), url))


def main():
  g1 = gevent.spawn(my_download, 'http://www.google.cn')
  g2 = gevent.spawn(my_download, 'http://www.qq.com')
  g3 = gevent.spawn(my_download, 'http://www.baidu.com')

  gevent.joinall([g1, g2, g3]) # 等待指定的協(xié)程結(jié)束


if __name__ == "__main__":
  main()

# 結(jié)果如下:
# GET http://www.google.cn
# GET http://www.qq.com
# GET http://www.baidu.com
# 下載 102221 bytes from http://www.baidu.com
# 下載 52297 bytes from http://www.qq.com
# 下載 3213 bytes from http://www.google.cn

#從上能夠看到是先獲取baidu的相關(guān)信息,然后依次是qq
#google,但是收到數(shù)據(jù)的先后順序不一定與發(fā)送順序相同,
#這也就體現(xiàn)出了異步,即不確定什么時(shí)候會(huì)收到數(shù)據(jù),順序不一定.

Num08–>gevent版–TCP服務(wù)器

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author : xiaoke
import socket
import gevent
from gevent import monkey
#猴子補(bǔ)丁,將標(biāo)準(zhǔn)庫的涉及IO操作方法替換成gevent
monkey.patch_all()


# 需要為客戶端提供服務(wù)
def do_service(connect_socket):
  while True:
    # tcp recv() 只會(huì)返回接收到的數(shù)據(jù)
    recv_data = connect_socket.recv(1024)

    # if recv_data == b'':
    if len(recv_data) == 0:
      # 發(fā)送方關(guān)閉tcp的連接,recv()不會(huì)阻塞,而是直接返回''
      # print('client %s close' % str(client_addr))
      # s.getpeername()  s.getsockname()
      print('client %s close' % str(connect_socket.getpeername()))
      break
    print('recv: %s' % recv_data.decode('gbk'))

def main():

  listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  # 設(shè)置允許復(fù)用地址,當(dāng)建立連接之后服務(wù)器先關(guān)閉,設(shè)置地址復(fù)用
  listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

  my_addr = ('192.168.105.125', 8080)
  listen_socket.bind(my_addr)

  listen_socket.listen(5) # 設(shè)置套接字成監(jiān)聽,5表示一個(gè)己連接隊(duì)列長度
  print('listening...')

  while True:
    # 接受連接請求,創(chuàng)建連接套接字,用于客戶端連通信
    connect_socket, client_addr = listen_socket.accept() # accept默認(rèn)會(huì)引起阻塞
    # 新創(chuàng)建連接用的socket, 客戶端的地址
    # print(connect_socket)
    print(client_addr)

    # 每當(dāng)來新的客戶端連接,創(chuàng)建協(xié)程,由協(xié)程和客戶端通信
    coroutine_do_service = gevent.spawn(do_service, connect_socket)


if __name__ == "__main__":
  main()

總結(jié)

關(guān)于協(xié)程的問題,面試中好像也會(huì)時(shí)常被問到,大家一定要注意理解,概念,怎么去實(shí)現(xiàn)。

以上就是本文關(guān)于Python中協(xié)程用法代碼詳解的全部內(nèi)容,希望對大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站其他相關(guān)專題,如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!

相關(guān)文章

  • Python enumerate() 函數(shù)如何實(shí)現(xiàn)索引功能

    Python enumerate() 函數(shù)如何實(shí)現(xiàn)索引功能

    這篇文章主要介紹了Python enumerate() 函數(shù)如何實(shí)現(xiàn)索引功能,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-06-06
  • Python測試Kafka集群(pykafka)實(shí)例

    Python測試Kafka集群(pykafka)實(shí)例

    今天小編就為大家分享一篇Python測試Kafka集群(pykafka)實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-12-12
  • Python多線程:主線程等待所有子線程結(jié)束代碼

    Python多線程:主線程等待所有子線程結(jié)束代碼

    這篇文章主要介紹了Python多線程:主線程等待所有子線程結(jié)束代碼,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-04-04
  • Scrapy基于Python構(gòu)建強(qiáng)大網(wǎng)絡(luò)爬蟲框架實(shí)例探究

    Scrapy基于Python構(gòu)建強(qiáng)大網(wǎng)絡(luò)爬蟲框架實(shí)例探究

    這篇文章主要為大家介紹了Scrapy基于Python構(gòu)建強(qiáng)大網(wǎng)絡(luò)爬蟲框架實(shí)例探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01
  • Python三十行代碼實(shí)現(xiàn)簡單人臉識(shí)別的示例代碼

    Python三十行代碼實(shí)現(xiàn)簡單人臉識(shí)別的示例代碼

    這篇文章主要介紹了Python三十行代碼實(shí)現(xiàn)簡單人臉識(shí)別的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • python 抽象類的使用詳解

    python 抽象類的使用詳解

    這篇文章主要介紹了python 抽象類的使用詳解,幫助大家更好的理解和學(xué)習(xí)使用python,感興趣的朋友可以了解下
    2021-03-03
  • Python從視頻中提取音頻的操作

    Python從視頻中提取音頻的操作

    這篇文章主要介紹了Python從視頻中提取音頻的操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • python 實(shí)現(xiàn)分頁顯示從es中獲取的數(shù)據(jù)方法

    python 實(shí)現(xiàn)分頁顯示從es中獲取的數(shù)據(jù)方法

    今天小編就為大家分享一篇python 實(shí)現(xiàn)分頁顯示從es中獲取的數(shù)據(jù)方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-12-12
  • Python 中的with關(guān)鍵字使用詳解

    Python 中的with關(guān)鍵字使用詳解

    這篇文章主要介紹了Python 中的with關(guān)鍵字使用詳解的相關(guān)資料,在Python中,with關(guān)鍵字是一個(gè)替你管理實(shí)現(xiàn)上下文協(xié)議對象的好東西,需要的朋友可以參考下
    2016-09-09
  • python 如何比較字符串是否一樣

    python 如何比較字符串是否一樣

    這篇文章主要介紹了python 如何比較字符串是否一樣的操作,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06

最新評論

邯郸县| 阿克| 郓城县| 改则县| 古浪县| 吴堡县| 衡东县| 阳泉市| 新郑市| 广平县| 东阳市| 金沙县| 长子县| 青阳县| 通江县| 乃东县| 庐江县| 峡江县| 黄冈市| 大田县| 信宜市| 崇明县| 兴城市| 宁德市| 逊克县| 连云港市| 崇阳县| 鲁甸县| 施甸县| 桃源县| 西林县| 石河子市| 尼玛县| 贵德县| 轮台县| 西充县| 武宣县| 溧阳市| 美姑县| 永新县| 泽州县|