Python多任务&进程&线程

**

一、多任务

**
1、多任务就是同一时刻多个任务同时执行。
例如开车看路的同时操作方向盘,对于电脑来说就是运行多个程序,例如浏览器,QQ,音乐盒同时运行。
2、电脑实现多任务的原理
例如三个程序同时运行是因为CPU在多个应用程序之间高速切换的结果,CPU在多个程序之间快速往返执行,我们肉眼根本看不到卡顿,导致我们的错觉感觉是同时运行的结果,如果电脑运行的时候会出现卡顿,就是因为CPU切换不过来了。
3、单核,双核CPU介绍:
单核CPU指的是CPU中有一个核心(形象理解CPU是人的头,核心是头里面包含的大脑),用来处理程序。
双核/四核CPU就是CPU中有2个或者4个核心,(一个脑袋中长了2个或者4个大脑)相当于有2个单核CPU或者是4个单核CPU。
4、在python中实现多任务有三种方式,进程、线程、协程

**

二、进程

**
1、什么是进程
电脑上运行应用程序时,双击就会打开,当我们双击时,操作系统将程序装载到内存中,操作系统为它分配资源,然后才能运行。运行起来的应用程序就称之为进程,也就是说当程序不运行的时候我们叫他程序,运行的时候就就是进程;程序和进程的对应关系是:程序只有一个,但是进程可以多个,例如QQ可以打开多个界面。
2、创建多进程
1、不使用多进程实现控制台,打印唱歌然后再打印跳舞

import time#调用 time模块
def sing():
    for i in range(3):
        print('------正在唱歌--------')
        time.sleep(1)
def dance():
    for i in range(3):
        print('-------跳舞----------')
        time.sleep(1)
if __name__=='__main__':
    sing()
    dance()

2、使用进程让唱歌和跳舞一起执行。

import time,multiprocessing

def sing():
    for i in range(3):
        print('------唱首歌-------')
        time.sleep(1)

def dance():
    for i in range(3):
        print('-------正在跳舞-------')
        time.sleep(1)

def main():
    p1=multiprocessing.Process(target=sing)
    p2=multiprocessing.Process(target=dance)
    p1.start()
    p2.start()
if __name__ == '__main__':
    main()#唱歌跳舞同时执行,提高了运行效率

3、进程的状态
在程序运行的过程中,由于被操作系统的调度算法控制,程序会进入几个状态:就绪,运行和阻塞
在这里插入图片描述
4、进程之间通讯
进程可以理解为复制了一份程序有加载到了内存了,进程之间是独立的,如果我想两个进程之间进行通讯怎么办呢?我们可以使用Queue 队列,队列是一种先进先出的存储数据结构,两个进程通讯,就是一个子进程往queue中写内容,另一个进程从queue中取出数据。就实现了进程间的通讯了。
.queue队列
1)q=multiprocessing.Queue(3)#3表示只能存放3个数据
2)put()方法,向队列中存放数据。如果队列已满,此方法将
组塞,知道队列中有项目可用为止。
3)get()返回q中的一个项目,如果q为空,此方法将阻塞,直到队列中有项目可用为止
4)get_nowait():不等待,直接抛出异常
5)full()如果q已满,返回为True
6)q.empty()如果调用此方法时q为空,返回True
7)q.qsize()查看队列中数据个数
下面将上述方法演练一下:

import multiprocessing#调用模块

q=multiprocessing.Queue(3)#创建一个队列对象
q.put('hello')#存入数据
q.put(123)
q.put([1,2,3])
# q.put('aaaa')#存多了,堵塞
print("有多少数据:",q.qsize())#查看队列中数据个数
print(q.full())#判断队列数据是否存满
print(q.get())#取出一个数据
print(q.get())
print(q.get())
#print(q.get())#多一个取出也会堵塞
print(q.empty())#判断队列数据是否空了
print(q.get_nowait())#不等待,抛出异常

练习1使用queue模拟多任务下载和处理数据
**注意:**传参的时候args=()括号里面的一定要放元组类型

import multiprocessing,time#调用模块

def download_data(q):
    data=['aa','bb','cc']
    for i in data:
        q.put(i)#将从列表里面下载的数据保存
    print('下载完了')

def processing_data(q):
    for i in range(q.qsize()):
        print(q.get())#将保存的数据取出来
    print('处理完了')

def main():
    q=multiprocessing.Queue()
    q1=multiprocessing.Process(target=download_data,args=(q,))#子进程1,传参元组类型
    q2=multiprocessing.Process(target=processing_data,args=(q,))#子进程2
    q1.start()#开启进程1
    time.sleep(1)#让进程2晚1秒开启
    q2.start()#开启进程2

if __name__=='__main__':
    main()#主进程

**

二、线程

**
线程概念
由于进程是资源拥有者,创建、撤消与切换存在较大的内存开销,因此需要引入轻型进程即线程,进程是资源分配的最小单位,线程是CPU调度的最小单位(程序真正执行的时候调用的是线程),每一个进程中至少有一个线程。 
知识点:
1、Python3 线程中常用的模块为:threading模块
2、线程可以传递参数。
3、.join()方法:
join()方法功能:当前线程执行完后其他线程才会继续执行。
4、.setDaemon()方法:
setDaemon()将当前线程设置成守护线程来守护主线程:当主线程结束后,守护线程也就结束,不管是否执行完成。
应用场景:qq多个聊天窗口,就是守护线程
注意:需要在子线程开启的时候设置成守护线程,否则无效
5、实例方法:
线程对象的一些实例方法

  • getName(): 获取线程的名称。
  • setName(): 设置线程的名称。
  • isAlive(): 判断当前线程存活状态。
    下面我们用threading模块创建线程,并且加入上述的各种方法
import time,threading
def sing(num):#传参num
    for i in range(num):
        print('唱歌')
        time.sleep(1)

def dance(num):#传参num
    for i in range(num):
        print('跳舞')
        time.sleep(1)

def main():
    p1=threading.Thread(target=sing,args=(3,))#args传参
    p2=threading.Thread(target=dance,args=(3,))
    # p1.setDaemon(True)#守护线程p1
    # p2.setDaemon(True)#守护线程p2

    print(p1.is_alive())#判断p1线程存活状态。
    p1.start()
     # p1.join()#加在这里在p2之前加载,p1执行完后,p2和主线程才执行
    print(p2.is_alive())#判断p2线程存活状态。
    p2.start()
    #p1.join()#p1,p2执行完后主线程才执行


    # p1.setName('张飞')#设置线程的名称
    # p2.setName('关羽')#设置线程的名称
    
    # p1_name = p1.getName()#获取线程的名称
    # p2_name = p2.getName()#获取线程的名称
    # print(p1_name)#打印获取线程的名称
    # print(p2_name)#打印获取线程的名称

if __name__=='__main__':
    main()
    print('少林功夫加唱歌跳舞有没有搞头') 

#子线程和主线程谁先抢到CPU谁执行

6、使用继承方法开启线程
1)定义一个继承hreading.Thread类
2)复写父类的run()方法

import threading#调用模块

class MyThread(threading.Thread):#定义继承一个类
    def __init__(self,num):
        super().__init__()#调用父类__init__,继承传参
        self.num=num

    def run(self):
        for i in range(self.num):
            print('唱歌',i)

if __name__=='__main__':
    my_thread=MyThread(3)#创建对象,正常方式传参
    my_thread.start()#开启子线程

7、线程之间共享全局变量

import threading
g_num=100#设定一个全局变量

def test1():
    global g_num#修改全局变量
    g_num+=1
    print('test1-----',g_num)

def test2():
    print('test2------',g_num)#打印全局变量

def main():
    p1=threading.Thread(target=test1)
    p2=threading.Thread(target=test2)
    p1.start()
    p2.start()

if __name__=='__main__':
    main()
    print('main...',g_num)

8、共享全局变量存在的问题
多线程开发的时候共享全局变量会带来资源竞争效果,也就是数据不安全。
例如看下方代码:

import threading
g_num=0

def test1(num):
    global g_num
    for i in range(num):
        g_num+=1
    print('test1-----',g_num)

def test2(num):
    global g_num
    for i in range(num):
        g_num+=1
    print('test......',g_num)

def main():
    p1=threading.Thread(target=test1,args=(10000000,))
    p2=threading.Thread(target=test2,args=(10000000,))
    p1.start()
    p2.start()

if __name__=='__main__':
    main()

上面代码运行时产生的效果是:
test… 11510621
test1----- 12112380
为什么会这样呢?直白一点说就是多个线程同时操作全局变量,争夺CPU造成数据不全,数据产生覆盖现象,从而造成数据不准确。为了线程同步能够保证多个线程访问竞争资源(全局内容),最简单的同步机制就是使用互斥锁。互斥锁保证了每一次只有一个线程进入写入操作,从而保证了多线程下数据的安全性。

import threading
g_num=0

def test1(num):
    global g_num
    lock.acquire()#加锁
    for i in range(num):
        g_num+=1
    lock.release()#解锁
    print('test1-----',g_num)

def test2(num):
    global g_num
    lock.acquire()#加锁
    for i in range(num):
        g_num+=1
    lock.release()#解锁
    print('test......',g_num)

lock=threading.Lock()#我们要创建一个全局锁的对象

def main():
    p1=threading.Thread(target=test1,args=(10000000,))
    p2=threading.Thread(target=test2,args=(10000000,))
    p1.start()
    p2.start()

if __name__=='__main__':
    main()

猜你喜欢

转载自blog.csdn.net/qq_44240254/article/details/86581531