进程和线程

  • 进程:进程是计算机中程序正在执行的实例,是系统进行资源分配和调度的基本单位。
  • 线程:也被称为轻量级进程,是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针,寄存器集合和堆栈组成。

线程和进程的关系:

python中线程和进程(一)-LMLPHP

进程和程序的关系:

Python中的线程

1. Thread类

python中线程的开发使用标准库:threading

def __init__(self, group=None, target=None, name=None,
             args=(), kwargs=None, *, daemon=None):
  • target:线程调用的对象,即目标函数
  • name: 线程名
  • args:为目标函数传递的实参,元组
  • kwargs:为目标函数关键字传参,字典

2. 线程的启动

import threading


def worker():
    print("I'm working")
    print("Finished")

t = threading.Thread(target=worker,name='worker') #创建线程对象
t.start() # 启动线程

可以通过threading.Thread创建一个线程对象,target指定目标函数,然后调用start方法进行启动线程。

3. 线程的传参

import threading
import time


def add(x,y):
    print("{} + {} = {}  线程ID:{}".format(x,y,x+y,threading.current_thread().ident))

thread1 = threading.Thread(target=add,name='add1',args=(4,5)).start()
time.sleep(1)
thread2 = threading.Thread(target=add,name='add2',args=(10,),kwargs={'y':5}).start()
time.sleep(1)
thread3 = threading.Thread(target=add,name='add3',kwargs={'x':20,'y':5}).start()

线程传参和函数传参没有什么区别,本质上就是函数传参。

4. 线程的属性和方法

threading的属性和方法

Thread实例的属性和方法

start和run的区别:
使用start启动线程,会启动一个新的线程,而使用run方法,并没有启动新的线程,只有在主线程上调用一个普通的函数。

5. daemon线程和non-daemon线程

在Python中,构造线程时,可以设置daemon属性。默认daemon线程是None,即non-daemon线程。

import time
import threading


def foo():
    time.sleep(5)
    for i in range(20):
        print(i)

t = threading.Thread(target=foo,daemon=True) #修改成None
t.start()
print("main thread Exit")

如果一个程序中只有daemon线程,那么主线程退出的时候,会结束所有的daemon线程,退出。
总结:
在一个程序中,如果有non-daemon线程的时候,主线程退出时,不会杀掉所有的daemon线程,直到所有的non-daemon线程全部结束,如果还有daemon线程,主线程退出的时候,会结束所有的daemon线程,然后退出。

6. join方法

import time
import threading


def foo(n):
    for i in range(n):
        print(i)
        time.sleep(1)

t = threading.Thread(target=foo,args=(10,),daemon=True)
t.start()
t.join() # 设置join
print("main thread Exit")

设置join后,daemon线程执行完了,程序才会退出。
join(timeout=None):
一个线程中调用另一个线程的join方法,此时调用者将会被阻塞,直到被调线程终止。一个线程可以被join多次。调用谁的join方法,就要等谁。

import time
import threading


def bar():
    while True:
        time.sleep(1)
        print("bar")


def foo():
    print("t1's daemon = {}".format(threading.current_thread().isDaemon()))
    t2 = threading.Thread(target=bar)
    t2.start()
    print("t2's daemon = {}".format(t2.isDaemon()))


t1 = threading.Thread(target=foo,daemon=True)
t1.start()
time.sleep(3)
print("main thread exiting")

只要主程序退出,2个工作线程就结束。

import time
import threading


def bar():
    while True:
        time.sleep(1)
        print("bar")


def foo():
    print("t1's daemon = {}".format(threading.current_thread().isDaemon()))
    t2 = threading.Thread(target=bar)
    t2.start()
    print("t2's daemon = {}".format(t2.isDaemon()))
    t2.join()


t1 = threading.Thread(target=foo,daemon=True)
t1.start()
t1.join()
time.sleep(3)

print("main thread exiting")

通过相互调用join方法,使线程结束不了。

7. 定时器Timer

class Timer(Thread):
    def __init__(self, interval, function, args=None, kwargs=None):

threading.Timer继承自Thread,该类用来定义多久执行一个函数。
start方法执行之后,Timer对象就会处于等待状态,等待了interval之后,开始执行function函数,如果在执行函数之前的等待阶段,使用了cancel方法,就会跳过执行函数。
但是如果线程中的函数已经开始执行,cancel就没有效果了。

import threading
import logging
import time


FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
logging.basicConfig(format=FORMAT,level=logging.INFO)


def worker():
    logging.info('in worker')
    time.sleep(2)

t = threading.Timer(5,worker)
t.setName('w1')
t.cancel()  # 提前取消
t.start()
print(threading.enumerate())
time.sleep(8)
print(threading.enumerate())
03-05 20:05