線程,進程協程在學習

進程:process 是計算機進行資源分配的和調度的基本單位,進程是程序的一次執行活動。容許多個程序加載到內存中實現併發。進程就是爲了實現CPU上執行多道程序提出的。python

線程:thread 是計算機可以進行運行調度的最小單位。是進程中的實際運行單位,進程中的每個線程可並行的執行不一樣的任務。一個進程包括至少一個線程。編程

協程:coroutine 是一種用戶態的輕量級線程,又稱爲微線程。能夠簡單理解爲單線程,可是擁有本身的寄存器上下文和棧,在不一樣的任務上切換。服務器

協程的好處:併發

  • 無需線程上下文切換的開銷
  • 無需原子操做鎖定及同步的開銷

    "原子操做(atomic operation)是不須要synchronized",所謂原子操做是指不會被線程調度機制打斷的操做;這種操做一旦開始,就一直運行到結束,中間不會有任何     context switch (切換到另外一個線程)。原子操做能夠是一個步驟,也能夠是多個操做步驟,可是其順序是不能夠被打亂,或者切割掉只執行部分。視做總體是原子性的核心。less

  • 方便切換控制流,簡化編程模型
  • 高併發+高擴展性+低成本:一個CPU支持上萬的協程都不是問題。因此很適合用於高併發處理。

缺點:異步

  • 沒法利用多核資源:協程的本質是個單線程,它不能同時將 單個CPU 的多個核用上,協程須要和進程配合才能運行在多CPU上.固然咱們平常所編寫的絕大部分應用都沒有這個必要,除非是cpu密集型應用。
  • 進行阻塞(Blocking)操做(如IO時)會阻塞掉整個程序

GIL:global interpreter lock 保證了同一個CPU同時只能有一條線程被執行。加全局鎖的主要緣由在於多年前的處理併發線程的過程當中極可能出現:與線程併發的內存’清理線程‘會錯將其餘線程的內存清理。爲了不這一現象因而粗糙的加上了全局鎖。可是在處理IO密集型操做時實際上是沒有影響的。socket

Gevent

實現協程的一個第三方庫,能夠輕鬆經過gevent實現併發同步或異步編程,在gevent中用到的主要模式是Greenlet, 它是以C擴展模塊形式接入Python的輕量級協程。 Greenlet所有運行在主程序操做系統進程的內部,但它們被協做式地調度。異步編程

import gevent
 
def func1():
    print('\033[31;1m函數1 第一步\033[0m')
    gevent.sleep(2)
    print('\033[31;1m函數1 第二步\033[0m')
 
def func2():
    print('\033[32;1m函數2 第一步\033[0m')
    gevent.sleep(1)
    print('\033[32;1m函數2 第二步\033[0m')
 
 
gevent.joinall([
    gevent.spawn(func1),
    gevent.spawn(func2),
])

結果:當遇到IO操做時自動切換。函數

函數1 第一步
函數2 第一步
函數2 第二步
函數1 第二步高併發

隊列通訊:RabbitMq

就算是進程之間的Queue可以實現進程之間的通訊,實際上是管道。可是也不能實現多個服務器之間不一樣語言之間的通訊。正所以纔是rabbitmq的功能體現之處。

send端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env python
import  pika
 
connection  =  pika.BlockingConnection(pika.ConnectionParameters(
                'localhost' ))
channel  =  connection.channel()
 
#聲明queue
channel.queue_declare(queue = 'hello' )
 
#n RabbitMQ a message can never be sent directly to the queue, it always needs to go through an exchange.
channel.basic_publish(exchange = '',
                       routing_key = 'hello' ,
                       body = 'Hello World!' )
print ( " [x] Sent 'Hello World!'" )
connection.close()

receive端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#_*_coding:utf-8_*_
__author__  =  'Alex Li'
import  pika
 
connection  =  pika.BlockingConnection(pika.ConnectionParameters(
                'localhost' ))
channel  =  connection.channel()
 
 
#You may ask why we declare the queue again ‒ we have already declared it in our previous code.
# We could avoid that if we were sure that the queue already exists. For example if send.py program
#was run before. But we're not yet sure which program to run first. In such cases it's a good
# practice to repeat declaring the queue in both programs.
channel.queue_declare(queue = 'hello' )
 
def  callback(ch, method, properties, body):
     print ( " [x] Received %r"  %  body)
 
channel.basic_consume(callback,
                       queue = 'hello' ,
                       no_ack = True )
 
print ( ' [*] Waiting for messages. To exit press CTRL+C' )
channel.start_consuming()

When RabbitMQ quits or crashes it will forget the queues and messages unless you tell it not to. Two things are required to make sure that messages aren't lost: we need to mark both the queue and messages as durable.

當rabbit退出或者崩潰的時候隊列和消息會被丟棄除非你設置一下。爲了確保消息不會丟失,咱們須要標記隊列和消息都是可持久化的。

一是聲明此隊列的時候在其後還有一個參數爲durable=True ,另個一個爲:

channel.basic_publish(exchange='',
                      routing_key="task_queue",
                      body=message,
                      properties=pika.BasicProperties(
                         delivery_mode = 2, 
                      ))

阻塞,非阻塞與異步

阻塞與非阻塞的關鍵點是:讀操做是否等待,而拷貝數據到用戶內存即寫操做都是會阻塞住的。

而異步指的是,只會發出命令後將其餘的操做交給其餘線程去作。整個過程永不會阻塞。

IO多路複用的表明select,poll,epoll是假的異步,屬於同步。不過是經過epoll負責輪詢全部的socket。

相關文章
相關標籤/搜索