6.19线程和多线程

  • 练习进程和线程的创建以及使用方法

实验步骤

多任务

 计算机早已进入多CPU或多核时代,而我们使用的操作系统都是支持“多任务”的操作系统

  • 我们可以同时运行多个程序
  • 可以将一个程序分解为若干个相对独立的子任务
  • 让多个子任务并发的执行,从而缩短程序的执行时间
  • 同时也让用户获得更好的体验。
  • 实现让程序同时执行多个任务也就是常说的“并发编程”

Python既支持多进程又支持多线程,因此使用Python实现并发编程主要有3种方式:

多进程、多线程、多进程+多线程。

1.并行与并发的区别

  • 并行:同时做某些事,可以互相不干扰的同一个时刻做几件事。(例如:高速公路,双向八车道,所有车都可以互不干扰的运行。)
  • 并发:同一时刻,有很多事情要做。(例如:乡村小路,只有一个车道,一个方向同一时间只能过一辆车。发生多辆车同时同行的情况就是并发。)
    • 可以用并行、队列、缓存区解决并发问题。
    • 排队是解决并发的办法之一。

2.并发的解决

”食堂打饭模型“

中午12点,开饭了,所有学生都涌向了食堂。这就是并发,如果人特别多,就是高并发。

1.队列、缓冲区

假设只有一个窗口,就只能排队打菜。

排队就是把人排成队列,解决了资源使用的问题。

排成的队列,其实就是一个缓冲地带,就是缓冲区

假设女士优先,窗口就要有两队,只要有女生就优先打饭,男生就等着,女生队伍就是一个优先队列

2.争抢

只开一个窗口,没有秩序,谁能挤进去就给谁打饭。窗口不能给其他人打饭。这是一种锁机制.

争抢也是一种解决高并发的方式,但是不好,因为有可能会有人很长时间抢不到。

3.预处理

如果打饭慢的原因是因为要现做的话,可以提前统计大多数人喜欢吃的菜品,提前做好,保证供应,提高打饭效率。

预处理的思想是加快用户获取数据的速度,常用缓存解决。

4.并行

由于吃饭的人数过多,一个窗口忙不过来,食堂老板决定扩大食堂,多开几个窗口,但是没有窗口都要雇人,造成成本上升。

企业可以通过购买更多服务器,或开启更多进程的方式做并行处理,来解决问题。(水平扩展)

注意:线程如果只在单个CPU核心上运行就并不是并行

5.提速

提高单个窗口的打饭速度,也是解决并发的方式。

提高打饭人员技能,或为单个窗口配置更多服务人员。

提高单机的性能,或者单个服务器安装更多CPU.

这是一种垂直提升的思路

6.多地就近原则

多开几个食堂,分流。可以把食堂设置在宿舍附近,来提升吃饭速度。

二.多线程

线程是CPU分配资源的基本单位。当一程序开始运行,这个程序就变成了一个进程,而一个进程相当于一个或者多个线程。当没有多线程编程时,一个进程相当于一个主线程;当有多线程编程时,一个进程包含多个线程(含主线程)。使用线程可以实现程序大的开发。

优点:

  1. 1.多个线程可以在同一个程序中运行,并且每一个线程完成不同的任务。
  2. 2.多线程实现后台服务程序可以同时处理多个任务,并不发生阻塞现象。
  3. 3.Python3 通过两个标准库 thread 和 threading 提供对线程的支持,由于thread只是为了兼容python2的thread模块,所以推荐使用threading模块实现多线程。
threading提供了如下方法:
  • run(): 用以表示线程活动的方法。

  • start():启动线程活动。

  • join([time]): 等待至线程中止。

三.创建线程

创建线程有两种方式

语法:

thread.Thread(group=Nore,targt=None,args=(),kwargs={},*,daemon=None)

  • 1.~group:必须为None,于ThreadGroup类相关,一般不使用。
  • 2.~target:线程调用的对象,就是目标函数。
  • 3.~name:为线程起这个名字。
  • 4.~args:为目标函数传递关键字参数,字典。
  • 5.~daemon(守护进程):用来设置线程是否随主线程退出而退出。


三.函数创建线程
 

import time
import threading
def test (x,y):
 for i in range(x,y):
     time.sleep(1)
     print(i)
thread1 = threading.Thread(name='t1',target= test,args=(1,10))
thread2 = threading.Thread(name='t2',target= test,args=(11,20))
thread1.start()   #启动线程1
thread2.start()   #启动线程2

1.类创建线程
 

import time
import threading
class mythread(threading.Thread):
  def run(self):
    for i in range(1,10):
      print(i)
      time.sleep(1)
thread1 = mythread()
thread2 = mythread()
thread1.start()
thread2.start()

2.主线程

  • 1.在python中,主线程是第一个启动的线程。线程包括主线程和子线程
  • ~父线程:如果启动线程A中启动了一个线程B,A就是B的父线程。
  • ~子线程:B就是A的子线程。
  • 2.创建线程时有一个damon属性,用它来判断主线程
  • 当daemon设置False时,线程不会随主线程退出而退出,主线程会一直等着子线程执行完;。当daemon设置True时,线程会随主线程退出而退出,主线程结束其他的子线程会强制退出。
  • 3.使用daemon注意:

~daemon属性必须在start( )之前设置,否则会引发RuntimeError异常

~每个线程都有daemon属性,可以显示设置也可以不设置,不设置则取默认值None

~如果子线程不设置daemon属性,就取当前线程的daemon来设置它。子子线程继承子线程的daemon值,作用和设置None一样。

~从主线程创建的所有线程不设置daemon属性,则默认都是daemon=False。

4.代码
 

import time
import threading
def tes1():
 time.sleep(10)
 for i in range(10):
  print(i)
thread1 = threading.Thread(target=tes1,daemon=False)
thread1.start()
print('主线程完成了')
# 解释:当主线程运行完毕输出完之后,等待一下后输出0~9。如果将daemon=False改为daemon=True,则不会运行for i in range(10)语句。

四、阻塞线程

一个线程中调用另一个线程的join方法,调用者被阻塞,直到调用线程被终止。

1.语法形式:

join(timeout-=None)


2.timeout 参数指定调用者等待多久,没有设置时,就一直等待被调用线程结束被调用线程结束。

其中,一个线程可以被join多次调用。
 

import time
import threading
def tes1():
 time.sleep(5)
 for i in range(10):
  print(i)
thread1=threading.Thread(target=tes1)
thread1.start()
thread1.join()
print('主线程完成了')



在thread1.start()后加thread1.join()添加join方法,输出时,主线程就会等待输出完0~9后再执行自己的print输出

多进程

众所周知,python的多线程提供的只是并发性,不会实际加快运行效率。而多进程则是用资源换取效率,可以实现真正的并行性。对于程序员而言,掌握多进程非常有必要。

多线程分为单线程和多线程

# 单线程
有一碗汤和一碗饭,正常操作是一口饭一口汤(当然你先把汤或者饭一口气吃完,当我无话可说)。单进程只能是先吃完饭,再喝汤;或者是先喝完汤,再吃饭。

import time
def eat():
    for i in range(3):
        print("吃饭……")
        time.sleep(1)
def drink():
    for i in range(3):
        print("喝汤……")
        time.sleep(1)
eat()
drink()



#先吃完所有饭,再喝完了所有汤 ,显然这种结果不是我们想要的

一.实现多线程
1.导入包

 

import multiprocessing


2、通过进程类创建进程对象
 

进程对象 = multiprocessing.Process(target=*)  此处target的值可以是函数名称


3、启动进程执行任务
 

进程对象.start()



# 多线程实现吃饭喝汤交叉进行
 

import multiprocessing
import time
def drink():
    for i in range(3):
        print("喝汤……")
        time.sleep(1)
def eat():
    for i in range(3):
        print("吃饭……")
        time.sleep(1)
if __name__ == '__main__':
    # target:指定函数名
    drink_process = multiprocessing.Process(target=drink)
    eat_process = multiprocessing.Process(target=eat)

    drink_process.start()
    eat_process.start()

带参多进程

用到的是args和kwargs,其中args是使用元素组的方式给指定任务传参,kwargs是使用字典方式给指定任务传参。

args使用方式
 

进程对象 = multiprocessing.Process(target=*,args=(*,))



kwargs使用方式
 

进程对象 = multiprocessing.Process(target=*,kwargs={"变量名": 变量值})



 

import time
import multiprocessing
def eat(num, name):
    for i in range(num):
        print(name + "吃一口……")
        time.sleep(1)
def drink(num, name):
    for i in range(num):
        print(name + "喝一口……")
        time.sleep(1)
if __name__ == '__main__':


    # target:指定执行的函数名
    # args:使用元组方式给指定任务传参
    # kwargs:使用字典方式给指定任务传参
   

 eat_process = multiprocessing.Process(target=eat, args=(3, "阿giao"))
    drink_process = multiprocessing.Process(target=drink, kwargs={"num": 4, "name": "阿giao"})

五.进程的编号 id

获取进程编号的目的是验证主进程和子进程的关系,可以得知子进程是由那个主进程创建出来的。

获取进程编码的两种操作:

  • os.getpid() 获取当前进程编号。

  • os.getppid() 获取当前父进程编号。

import time
import multiprocessing
import os
def dance():
    # 获取子进程id,因为当前函数就是子进程
    print('dance子进程id:',os.getpid())
    # 获取dance子进程的父进程
    print('dance的父进程:',os.getppid())
    for i in range(5):
        time.sleep(1)
        print("跳舞",i)
def sing():
    # 获取子进程id
    print('sing子进程id:',os.getpid())
    # 获取sing子进程的父进程
    print('sing的父进程:',os.getppid())
    for i in range(5):
        time.sleep(1)
        print("唱歌",i)
if __name__ == '__main__':
    # 获取当前主进程的id编号
    print('主进程id:',os.getpid())
    my_dance = multiprocessing.Process(target=dance)
    my_sing = multiprocessing.Process(target=sing)
    my_sing.start()
    my_dance.start()

六.线程池和进程池

concurrent

concurrent.futures

Python 3.x版本中引入的模块

from concurrent import futures
 
futures.ThreadPoolExecutor()  # 异步调用线程池
futures.ProcessPoolExecutor()  # 异步调用进程池

使用的时候首先要定义一个池的执行器对象,Executor类子类对象。

方法

作用

ThreadPoolExecutor(max_worker=1)

池中至多创建max_workers个线程池来同时异步执行,返回Executor实例

submit(fn,*args,**kwargs)

提交执行的函数及其参数,返回Future实例

shutdown(wait=True)

清理池

i

mport time
from concurrent.futures import ThreadPoolExecutor
def tes1(max):
    time.sleep(1)
    print(max)
with ThreadPoolExecutor(max_workers=2)as pool:
    result = pool.map(tes1,('hello','world','你好','世界'))
    for r in result:
        print(r)     

import time
from multiprocessing import Pool
def tes1(max):
    print(max)
    time.sleep(3)
if __name__ =='__main__':
    pool = Pool(processes=10)
    for i in range(100):
        pool.apply(tes1,args=(i,))
    print('完成')
    pool.close()
    pool.join()

  • 15
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值