并发编程——多线程多进程

一.并发编程

1.并发

并发:同时接到多个任务,同时执行多个任务,但是具体到某一时刻,只是在执行某一个任务,只是在短时间内在多个任务之间进行切换,模拟形成多个任务同时执行的现象。

并发

2.并发编程

并发编程:在一台处理器上同时执行多个任务,在某一时刻只执行其中的一个任务,相较于顺序而言,并发极大的了提高执行效率。

3.python对并发编程的支持

      python长久以来一直支持不同方式的并发编程,包括线程、子进程以及利用其他生成器的并发实现, python在大部分系统上同时支持消息传递和基于线程的并发编程机制。python使用内部全局解释器锁GIL来保证线程安全,GIL同时只允许一个线程执行。

      python提供了一些工具用于管理基于线程和进程的并发操作,即使是简单的程序也能够使用这些工具使得任务并发进行从而加快运行速度。threading模块为并发操作提供了一系列高级的、面向对象的API。thread对象在一个进程内并发的运行,分享内存资源

python中的并发编程支持:
      多线程并发机制——单进程多线程:如果多个执行单元需要频繁访问公共数据的时候【多线程并发】

      多进程并发机制——多进程单线程:如果多个执行单元执行不同任务同时访问不同的独立数据【多进程并发】

      协程并发机制——单进程单线程:并发:通过程序代码的方式,完成模拟CPU时间片并发任务的处理机制

二.进程与线程

1. 线程与多线程编程

1.1什么是线程

      线程:计算机程序运行中的实际执行者就是线程,线程又称为轻量级进程,是一个CPU的执行单元,每个进程至少会有一个主线程用于执行程序

1.2多线程编程

      操作方式:python本身支持多任务处理,并提供了三种操作方式:
                          1)多线程任务处理机制
                           2)多进程任务处理机制
                           3)协程多任务处理机制
                           python官方推荐的_threading模块的多线程并发机制编程主要有两种操作方式:
                           1)函数式的线程创建方式,适合面向过程中的并发编程的实现
                           2)面向对象式编程,适合面向程序的并发编程的实现

      多线程:通过threading模块中的Thread函数,可以实现多线程程序的运行。单线程实现模式就是在同一时间有且只有一个线程正在执行,多线程实现模式是在同一时间可以有多个线程同时执行,因此在效率上多线程要远远高于单线程。

      线程状态:线程拥有两种状态,分别为join和daemon。Join状态是独占模式,独占CPU的运行单元,只有在此线程完成或超时后才能继续运行其他的线程。Daemon属性是用于标识某个线程是否守护线程。守护线程是跟主线程紧密相连的,即当主线程退出时,无论守护线程是否执行完成,都会随主线程一起退出。

      线程锁:在多个线程同时执行时,可能会由于访问的是同一种数据而造成线程之间的冲突,一旦线程之间冲突,就会造成一些错误的出现。为了避免这种不必要的现象的出现,锁的存在就很有必要了,在多个线程同时访问某一数据时,给当前某一线程正在访问的数据上锁,使得其他进程无法访问,避免数据冲突的发生,当线程执行结束后再给这一段数据解锁,下一进程才可继续访问。虽然线程锁能够帮助我们避免数据冲突的问题,但凡事都有利有弊,如果在上锁的过程中没有一个良好的认知的话,会使得这个锁成为死锁,致使数据重复上锁从而无限循环执行无法进入下一步的访问。

      线程事件:事件Event用于多个线程之间的互相通信,它可以被多个线程访问,可以标记不同的状态,并且能用于控制线程等待运行之间的转换。在线程执行时,使用event.wait()使线程进入等待状态,通过event.set()标记某一线程,标记之后继续执行,同时,在转换的过程中,需要合理运用event.clear()清除标记防止线程执行顺序的错乱

      线程条件:多个线程互相通信时,可能会根据实际情况进行线程之间的等待与执行的切换,在python中,通过condition()进行条件的判断,只有当满足条件时,事件才会在等待与执行中进行转换

      线程队列:线程之间共享数据的访问问题和线程之间的通信是线程管理中的主要问题。为了更好的解决这一问题。Python提供了一个数据类型,队列Queue(),队列主要用于多线程并发模式下,安全的访问数据且不会造成数据之间的冲突,通过向队里中添加数据和获取数据达到线程和谐执行的目的。

      面向对象多线程:面向对象的多线程,通过重写Thread类型的run()方法,创建自定义线程的对象后,调用start()方法启动

2.进程与多进程编程

2.1什么是进程

      进程:通俗的来说,一个进程计算机上正在运行的一个程序,是计算机中一个程序在一个数据集上一次动态的执行过程,主要包含三部分内容:
                    1)程序:描述进程的功能以及处理流程
                    2)数据集:功能处理过程中需要的资源数据
                    3)进程控制:严格控制进程执行过程中的各种状态

2.2多进程编程

      进程:进程是正在执行的应用程序,一个进程包含了应用程序的所有信息,一个应用程序通过其功能的多样性,可以通过多个进程并发来实现。 多进程的面向对象实现:多进程的面向对象实现类似于多线程,自定义类型进程,通过继承系统标准类型multiprocessing Process ,重写父类的run()方法,在方法中定义执行代码,使用创建的自定义类型进程的独象,调用对象的start()方法启动一个新的进程

      带参数的多进程:在多进程的操作模式下,全局变量是共享的,在方便操作的同时,也为数据的修改带来诸多不便。此时,我们可以通过两种方法进行观察多进程模式下的数据处理。其一为全局变量的数据,在多个进程执行的过程中,每个进程本身都相当于是一个相互独立运行的程序,每个进程中全局数据的变量都是相互独立的;其二为参数传递,给多进程并发处理函数传递参数,并不能使数据被多个进程共享,函数执行并发操作时,每一个进程都会独自拷贝一份全局变量进行使用,从而使多个进程使用的同一个全局变量不互相影响

      内置进化池:内置进化池是多线程的简化,是面向过程多进程的操作优化方式。有了进程池后,可以简单的模拟多任务并行的操作,使多个任务被几个进程平均分配进行处理

      多个进程通信:不同线程之间的数据通信,涉及到核心的数据共享问题,主要由multiprocessing manager类型实现,该类型内置了大量的用于数据共享的操作。多个进程之间的通信操作,数据的传递由multiprocessing模块中的队列queue实现,同时,此模块中的pipe可以更加有好的进行多个进程之间的通信

2.3.进程与线程的对比

      对比如下:
                    1)一个进程可以有多个线程,但至少有一个主线程
                    2)一个线程只能属于一个进程
                    3)一个进程中多个线程,可以共享进程中提供的数据
                    4)CPU运算分配给线程,CPU执行运算的是线程
                    5)线程是最小的运行单元,进程是最小的资源管理单元

三.python中的模块

1.模块的作用

模块:

1)在一个python模块中可以包含的数据有变量、函数和类型等,是一个完整独立的代码块

2)独立模块中的变量分为全局变量和局部变量,全局变量是整个模块的变量,可以被其他模块引入使用;局部变量是当前模块中某个函数或类型的变量,只能被当前的函数及类型使用

3)模块被其他模块引入后,相当于在当前模块重写了被引入模块的代码,所以会执行被引入模块中的所有代码

4)模块中的测试代码可以包含在if name == “main”: 这样的语句中,在这些语句中的模块不会在引入其他模块时执行

5)一个标准模块的定义方式分为以下几个步骤

●#coding:utf-8)

● 引入系统标准模块

● 引入第三方模块

●引入自定义模块

● 声明定义变量

●声明定义函数

●声明定义类型

● 当前模块测试代码
模块的作用:
1)一个.py文件就是一个模块,模块有利于函数的维护,方便函数的调用

2)避免函数名和变量名冲突,每个模块独立的命名空间

3)可以定义函数、类和变量,模块里也能包含可执行的代码

2.模块中常见的类型/方法/函数

TODO

3._thread模块

thread类型属性和方法:

名称 描述
init(group,target,name,args,kwargs) 构造方法、创建线程类型
is_alive()/isAlive() 判断当前线程是否 alive 状态
run() 线程执行方法,自定义线程必须重写该函数
start() 线程启动方法
join([timeout=None]) 线程独占,等待当前线程运行结束或者超时
ident 标识当前线程的唯一编号
name 当前线程名称
daemon 布尔值,判断当前线程是否守护线程

注意事项

同一个对象不能被多个线程操作*
* 线程的操作主体都是Thread对象(或者是它的子类)由这个对象执行start方法* 
区别在于 
- 继承Thread类之后,直接就用这个子类去执行start方法 
- 实现Runnable接口的话,是将这个类作为参数传入到Thread的构造方法里面,再由Thread来执行start方法。
所以说,线程操作的主体都是Thread对象或者是它的子类对象。

简单实现

# -*- coding: utf-8 -*-
# 引入依赖的模块

import _thread,time

# 定义处理函数

def sing():
    for i in range(10):
        print("唱歌。。。。。。。。。。。。")

def dance():
    for j in range(10):
        print("跳舞........................")

# 单线程
_thread.start_new_thread(sing,())
_thread.start_new_thread(dance,())

time.sleep(1)

4.threading模块

threading实现的面向函数的多线程:
●多线程购票程序[多个窗口同时售票]

# -*- coding: utf-8 -*-
#引入模块
import threading,time
ticket_count = 10
lock = threading.Lock()

def sale_ticket():
    # 导入全局变量
    global ticket_count
    time.sleep(0.5)
    while True:
        time.sleep(0.5)
        # 上锁
        if lock.acquire():
            if ticket_count > 0:
                ticket_count -= 1
                print(threading.current_thread().getName(),"售票1张,剩余票数:",ticket_count)

            else:
                # print( "窗口票已售空" + str(i))
                # threading.Thread(name="窗口" + str(i)+ '票已售空', target=sale_ticket)
                print(threading.current_thread().getName(), "票已售完")
                # 释放锁
                lock.release()
                break
            lock.release()


if __name__ == "__main__":
    for i in range(1,7):
        t = threading.Thread(name= "窗口"+str(i),target=sale_ticket)
        t.start()

●多线程-线程通信-事件[Event]-买早餐[核心:两个线程之间的数据依赖]

# -*- coding: utf-8 -*-
import threading,time

event = threading.Event()

def xf():
    print("小贩 :炸油条")
    time.sleep(0.5)
    print("小贩 :卖油条")
    time.sleep(1)
    event.set()
    event.clear()
    event.wait()
    print("小贩 :找钱")

    time.sleep(1)
    event.wait()
    print("小贩 :谢谢惠顾")

def gk():
    time.sleep(1)
    print("顾客 :买油条")
    time.sleep(1)
    print("顾客 :吃油条")
    time.sleep(1)
    print("顾客 :吃饱了")
    time.sleep(1)
    event.set()
    event.clear()
    print("顾客 :付钱")
    time.sleep(1)
    event.set()
    print("顾客 :我走了")


if __name__ == "__main__":
    x = threading.Thread(target=xf)
    g = threading.Thread(target=gk)
    x.start()
    g.start()

●多线程-线程通信-条件[Condition]-生产者消费者


# -*- coding: utf-8 -*-
import threading,time,random

con = threading.Condition()

basket = list()

def food():
    while True:
        con.acquire()
        if len(basket) >= 10:
            print(threading.current_thread().getName(),"菜已上满,请慢用")
            time.sleep(1)
            con.wait()

        else:
            cai = random.randint(0,5)
            print(threading.current_thread().getName(),"上菜",cai)
            time.sleep(1)
            basket.append(cai)
            con.notify()

        con.release()

def chi():
    while True:
        con.acquire()
        if len(basket) <= 0:
            print(threading.current_thread().getName(),"没菜了")
            time.sleep(1)
            con.wait()


        else:
            cai = basket.pop()
            print(threading.current_thread().getName(),"吃了",cai)
            time.sleep(1)
            con.notify()

        con.release()


if __name__  == "__main__":
    for i in range(2):
        a = threading.Thread(name="生产者" + str(i) +"号",target=food)
        a.start()

    for i in range(3):
        b = threading.Thread(name="消费者" + str(i) + "号",target=chi)
        b.start()

●多线程-数据容器-队列[Queue]-生产者消费者

# -*- coding: utf-8 -*-
import threading,time,random,queue
basket = queue.Queue(5)

def sc():
    while True:
        time.sleep(1)
        n = random.randint(0,5)
        try:
            basket.put(n,timeout=1)
            print("生产+1",n)
        except:
            print("蓝已满")

def xf():
    while True:
        time.sleep(1)
        try:
            n = basket.get(timeout=1)
            print("消费+1",n)
        except:
            print("蓝已空")


if __name__ =="__main__":
    for i in range(2):
        a = threading.Thread(target=sc)
        a.start()

    for j in range(1):
        b = threading.Thread(target=xf)
        b.start()

你可能感兴趣的:(并发编程——多线程多进程)