1、线程:
(1)讲程是分配资源的最小单位,一旦创建一个进程就会分配一定的资源,就像两个人聊OQ就需要打开两个QQ软件一样,是比较浪费资源的。
线程是程序执行的最小单位,实际上进程只负责分配资源,而利用这些资源执行程序的是线程,也就说进程是线程的容器,一个进程中最少有一个线程来负责执行程序,同时线程自己不拥有系统资源,只需要一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源.这就像通过一个QQ软件(一个进程)打开两个窗口(两个线程)跟两个人聊天一样,实现多任务的同时也节省了资源。
(2)线程的创建步骤
# 1.导入线程模块
import threading
# 2.通过线程类创建线程对象
线程对象 = threading.Thread(target=任务名)
# 3.启动线程执行任务
线程对象.start()
(3)通过线程类创建线程对象:线程对象 = threading.Thread(target=任务名)
参数说明:
target:执行的目标任务名,这里指的是函数名(方法名)
name:线程名,一般不用设置
group:线程组,目前只能使用None
(4)线程创建与启动的代码
# 创建子线程
sing_thread= threading.Thread(target=sing)
# 创建子线程
dance_thread = threading.Thread(target=dance)
# 启动线程
sing_thread.start()
dance_thread.start()
(5)线程执行带有参数的任务:args-以元组的方式给执行任务传参、kwargs-以字典方式给执行任务传参
# target: 线程执行的函数名
# args: 表示以元组的方式给函数传参
sing_thread = threading.Thread(target=sing, args=(3,))
sing_thread.start()
# target: 线程执行的函数名
# kwargs:表示以字典的方式给函数传参
dance_thread = threading.Thread(target=dance,kwargg={"count": 3})
#开启线程
dance thread.start()
(6)主线程和子线程的结束顺序
对比进程,主线程会等待所有的子线程执行结束后主线程再结束
(7)设置守护主线程:要想主线程不等待子线程执行完成可以设置守护主线程
#设置守护主线程方式1,daemon=True 守护主线程
work_thread= threading.Thread(target=work, daemon=True)
#设置守护主线程方式2
# work_thread.setDaemon(True)
work_thread.start()
#主线程延时1秒
time.sleep(1)
print ("over")
(8)获取当前的线程信息:
#通过current_thread方法获取线程对象
current_thread = threading.current_thread()
#通过current_thread对象可以知道线程的相关信息,例如被创建的顺序
print(current_thread)
(9)线程间的执行顺序:线程之间执行是无序的,是由CPU调度决定某个线程先执行的
2、进程和线程对比:
(1)线程是依附在进程里面的,没有进程就没有线程
(2)一个进程默认提供一条线程,进程可以创建多个线程
(3)创建进程的资源开销要比创建线程的资源开销要大
(4)进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位
(5)线程不能够独立执行,必须依存在进程中
(6)进程优缺点:可以用多核,但资源开销大
(7)线程优缺点:资源开销小,但不能使用多核
3、join方法,等待子线程结束
import threading
import time
def my_print(n):
for i in range(n):
print(i)
time.sleep(0.5)
t_lst = []
for i in range(3):
t = threading.Thread(target=my_print,args=(5,))
t_lst.append(t)
for t in t_lst:
t.start()
for t in t_lst:
t.join()
print('主线程')
4、threading.local:线程隔离,比如使全局变量在线程中隔离,当做局部变量使用,互不影响
(1)例子1
import threading
mylocal = threading.local()
mylocal.value = 0
def add_one(index):
mylocal.value = index
print(mylocal.value)
for i in range(1, 11):
t = threading.Thread(target=add_one,args=(i,))
t.start()
print(mylocal.value)
(2)例子2
import threading
import time
# 利用local类,创建一个全局对象 local_obj
local_obj = threading.local()
def func():
local_obj.var = 0
# 如果使用局部变量,函数调用需要传参
func_print()
def func_print():
for k in range(100):
time.sleep(0.01)
# 直接使用local_obj.var,自动获取当前线程对应的属性值
local_obj.var += 1
print(f'线程id:{threading.get_ident()},thread-local数据:{local_obj.var}')
# 创建3个线程,并启动
for th in range(3):
threading.Thread(target=func,).start()
print(mylocal.value)
5、获取线程id:threading.get_ident()
6、threading.Lock():线程加锁,加锁后必须释放后再加锁,不然会死锁
(1)操作:
m_lock = threading.Lock()
m_lock.acquire() # 加锁
m_lock.release() # 释放锁
(2)例子
import threading
import time
m_lock = threading.Lock()
a = 0
def worker():
time.sleep(1)
global a
for i in range(100000):
m_lock.acquire() # 加锁
a += 1
m_lock.release() # 释放锁
# 或者直接:with m_lock,不用加锁和释放锁,简化为这一步
thread_lst = []
for i in range(5):
t = threading.Thread(target=worker)
thread_lst.append(t)
for t in thread_lst:
t.start()
for t in thread_lst:
t.join()
print(a)
7、threading.RLock():线程加锁,可以连续加锁,几次加锁几次释放就行,不会死锁,安全
8、多线程线程同步—Condition,Condition被称为条件变量,提供以下方法
(1)acquire-加锁
(2)release-释放锁
(3)wait-调用wait方法后,waitting池会记录这个线程,同时,这个线程也进入到了阻塞状态,直到超时或者别的线程唤醒它
(4)notify-唤醒处于waiting状态的线程,被唤醒的这个线程会再次acquire锁,得到锁以后继续执行
import threading
import time
condition = threading.Condition()
products = 12
class Producer(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
global condition, products
while True:
if condition.acquire():
if products < 10:
products += 1;
print("Producer(%s):deliver one, now products:%s" %(self.name, products))
condition.notify()
else:
print("Producer(%s):already 10, stop deliver, now products:%s" %(self.name, products))
condition.wait();
condition.release()
time.sleep(2)
class Consumer(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
global condition, products
while True:
if condition.acquire():
if products > 1:
products -= 1
print("Consumer(%s):consume one, now products:%s" %(self.name, products))
condition.notify()
else:
print("Consumer(%s):only 1, stop consume, products:%s" %(self.name, products))
condition.wait()
condition.release()
time.sleep(2)
if __name__ == "__main__":
for p in range(0, 2):
p = Producer()
p.start()
for c in range(0, 10):
c = Consumer()
c.start()
9、python多线程同步-信号量-Semaphore:内部维护了一个计数器,每一次acquire操作都会让计数器减1,每一次release操作都会让计数器加1,当计数器为0时,任何线程的acquire操作都不会成功,Semaphore确保对资源的访问有一个上限,控制并发量
import threading
import time
semaphore = threading.Semaphore(2)
def worker(id):
print('thread {id} acquire semaphore'.format(id=id))
semaphore.acquire()
print('thread {id} get semaphore do something'.format(id=id))
time.sleep(2)
semaphore.release()
print('thread {id} release semaphore'.format(id=id))
for i in range(10):
t = threading.Thread(target=worker, args=(i, ))
t.start()
10、python多线程同步-事件Event:
(1)事件Event:灵活的协调线程间的操作,提供了下面几个方法:
(1-1)set():将事件内部标识设置为True,Event对象最初创建时,内部标识默认是False
(1-2)wait():当在线程中调用wait时,如果事件内部标识为False,则会阻塞,直到set方法被调用,将内部标识设置为True
(1-3)clear():将内部标识重新设置为False
(1-4)is_set():如果内部标识是True,则返回True,反之,返回False
。。。未完待续
备注:
进程、线程
1、进程:就是一个程序,运行在系统之上,那么便称之这个程序为一个运行进程,并分配进程ID方便系统管理
2、线程:线程是归属于进程的,一个进程可以开启多个线程,执行不同的工作,是进程的实际工作最小单位
3、操作系统中可以运行多个进程,即多任务运行。一个进程内可以运行多个线程,即多线程运行
4、进程之间是内存隔离的,即不同的进程拥有各自的内存空间。这就类似于不同的公司拥有不同的办公场所。
5、线程之间是内存共享的,线程是属于进程的,一个进程内的多个线程之间是共享这个进程所拥有的内存空间的。这就好比,公司员工之间是共享公司的办公场所。
6、并行执行:指的是同一时间做不同的工作。进程之间就是并行执行的,操作系统可以同时运行好多程序,这些程序都是在并行执行
7、线程也可以并行执行,比如一个Python程序,其实是完全可以做到:一个线程在输出:你好。一个线程在输出中Hello。像这样一个程序在同一时间做两件乃至多件不同的事情,我们就称之为:多线程并行执行
8、threading模块:python通过threading模块来实现多线程编程
import threading
thread_obj = threading.Thread([group[,target [,name[,args[,kwargs]]]]])
# group:暂时无用,未来功能的预留参数
# target:执行的目标任务名
# args:以元组的方式给执行任务传参
# kwarqs:以字典方式给执行任务传参
# name:线程名,一般不用设置
# 启动线程,让线程开始工作
thread_obj.start()
9、threading模块的使用
(1)thread_obj=threading.Thread(target=func) # 创建线程对象
(2)thread obj.start() # 启动线程执行