多线程

多线程

进程和线程

进程是资源分配的最小单位,线程是CPU调度的最小单位。每一个进程中至少有一个线程。

进程

本质上就是一段程序的运行过程(抽象概念)

最小的资源单位(操作系统分配cpu,内存资源的基本单位)

线程

最小的执行单元(实例),是cpu调度和分派的基本单位

每个线程都有自己的堆栈和局部变量

线程在同一进程中的各个线程,都可以共享该进程所拥有的资源

1.一个程序至少有一个进程,一个进程至少有一个线程。(进程可以理解为线程的容器),一个进程里可以开辟多个线程和进程

2.进程在执行过程中拥有独立的内存单元,而多个线程共享这个进程的内存,从而极大地提高了程序的运行效率

3.一个线程(主线程创建子线程)可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行

同一进程中的各个线程,都可以共享该进程所拥有的资源,这首先表现在:所有线程都具有相同的进程id,这意味着,线程可以访问该进程的每一个内存资源;此外,还可以访问进程所拥有的已打开文件、定时器、信号量机构等。由于同一个进程内的线程共享内存和文件,所以线程之间互相通信不必调用内核。

并发&并行

并发:系统具有处理多个任务的能力,cpu疯狂切换(单核)

并行:系统具备同时处理多个任务的能力(多核)

同步&异步

同步:我煮饭,我等煮好饭去炒菜--等

异步:我煮饭的过程中,可以去炒菜--不等

python解释器的GIL锁

GIL解决什么问题?

Python使用引用计数来管理内存,x=1,如果线程执行到1,这个时候垃圾回收线程来执行,会发现x引用计数为0,就会把x给干掉,显然,这样是不对的。

无论你启多少个线程,你有多少个cpu, Python在执行的时候会淡定的在同一时刻只允许一个线程运行

一个python进程同一时间只有一个线程能被cpu调用(多核对它基本没有用)

但是可以开启多个进程,然后每个进程就可以各有一个线程丢给一个cpu,来实现并行

线程抢占的就是GIL锁

计算密集型:多进程效率高

I/O密集型:多线程效率高(IO过程中cpu会有空闲时间,可以利用空闲时间做别的任务)

多线程或者(多进程+携程)用于IO密集型,如socket,爬虫,web

多进程用于计算密集型,如金融分析(但是不推荐)

python中的多线程

例1

如下有三个线程,一个主线程,主线程开辟了两个子线程

主线程最先运行完毕,t1子线程运行了3秒,t2子线程运行了6秒

整个进程花费了6秒多一点结束

import threading
import time

def zx(t,s):
    time.sleep(s)
    print(t)


if __name__ == '__main__':
    t1=threading.Thread(target=zx,kwargs={"t":"t1","s":3})
    t1.start()

    t1=threading.Thread(target=zx,kwargs={"t":"t2","s":6})
    t1.start()

    print("main")

main
t1
t2

Process finished with exit code 0
例2

和上面一样的知识点

import time
import threading

def music():
    print("开始听歌")
    time.sleep(3)
    print("停止听歌")

def game():
    print("开始玩游戏")
    time.sleep(6)
    print("停止玩游戏")

if __name__ == '__main__':
    t1=threading.Thread(target=music)
    t2=threading.Thread(target=game)
    t1.start()
    t2.start()

    print("main")

开始听歌
开始玩游戏
main
停止听歌
停止玩游戏

Process finished with exit code 0

常用方法和属性

threading模块提供的一些方法:
# threading.currentThread(): 返回当前的线程对象。
# threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
# threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

线程对象方法
#ident:线程的id
# run():  线程被cpu调度后自动执行线程对象的run方法
# start():启动线程活动。
# isAlive(): 返回线程是否活动的。
# getName(): 返回线程名。
# setName(): 设置线程名。

join() 线程等待

join():在子线程完成运行之前,这个子线程的父线程将一直被阻塞。

看懂下面代码者,方可大成

join()相当于,执行完这个子线程,再去执行主线程下面的代码

import time
import threading

def music():
    print("开始听歌")
    time.sleep(3)
    print("停止听歌")

def game():
    print("开始玩游戏")
    time.sleep(6)
    print("停止玩游戏")

if __name__ == '__main__':
    t1=threading.Thread(target=music)
    t2=threading.Thread(target=game)
    t1.start()

    t1.join()

    t2.start()

    t2.join()

    print("main")

开始听歌
停止听歌
开始玩游戏
停止玩游戏
main

Process finished with exit code 0

setDaemon()守护线程

setDaemon(True):

将线程声明为守护线程,必须在start() 方法调用之前设置,当我们 在程序运行中,执行一个主线程,主线程运行完毕,想退出时,会检验子线程是否完成。如果子线程未完成,则主线程会等待子线程完成后再退出。但是有时候我们需要的是 只要主线程完成了,不管子线程是否完成,都要和主线程一起退出,这时就可以 setDaemon方法

例1

当主线程运行完毕,去检验t1和t2,发现他们都是守护线程,直接退出

import time
import threading

def music():
    print("开始听歌")
    time.sleep(3)
    print("停止听歌")

def game():
    print("开始玩游戏")
    time.sleep(6)
    print("停止玩游戏")

if __name__ == '__main__':
    t1=threading.Thread(target=music)
    t2=threading.Thread(target=game)
    t1.setDaemon(True)
    t2.setDaemon(True)
    t1.start()
    t2.start()
    print("main")

开始听歌
开始玩游戏
main

Process finished with exit code 0

例2

当主线程运行完毕,去检验t1和t2,发现t1不是守护线程,t2是守护线程,t1耗时3秒,t1执行完毕,直接退出,因为t2耗时6秒,还是守护线程,主线程不用管它

import time
import threading

def music():
    print("开始听歌")
    time.sleep(3)
    print("停止听歌")

def game():
    print("开始玩游戏")
    time.sleep(6)
    print("停止玩游戏")

if __name__ == '__main__':
    t1=threading.Thread(target=music)
    t2=threading.Thread(target=game)
    t2.setDaemon(True)
    t1.start()
    t2.start()
    print("main")

开始听歌
开始玩游戏
main
停止听歌

Process finished with exit code 0

定时器

线程延迟执行

from threading import Thread,Timer
import time

def task():
    print('线程执行了')
    time.sleep(2)
    print('线程结束了')

t = Timer(4,task) # 过了4s后开启了一个线程
t.start()
print("end")
end
线程执行了
线程结束了

原文地址:https://www.cnblogs.com/zx125/p/11440563.html