进程在os中是分配资源的基本单位,也是能够独立运行的基本单位。
而线程则是cpu调度的基本单位,一个进程至少包含一个进程。
一个进程中的线程共享资源。
在Python中的多线程是真正的多线程而不是模拟出来的。
import os,threading
import time,random
def loop():
print('thread %s is runing' % threading.current_thread().name)
for i in range(5):
print('thread %s >>> %s' % (threading.current_thread().name,i))
time.sleep(random.random()*5)
print('thread %s is end' % threading.current_thread().name)
print('thread %s is runing...' % threading.current_thread().name)
t = threading.Thread(target=loop,name="LoopThread")
t.start()
t.join()
print('thread %s is end' % threading.current_thread().name)
输出
thread MainThread is runing...
thread LoopThread is runing
thread LoopThread >>> 0
thread LoopThread >>> 1
thread LoopThread >>> 2
thread LoopThread >>> 3
thread LoopThread >>> 4
thread LoopThread is end
thread MainThread is end
任何进程运行时默认会启动一个线程,我们把这个线程称为主线程,主线程又可以启动新的线程,Pythonde内置函数
threading.current_thread()会返回当前正在执行的线程,主线程实例的名称为MainThread,子线程创建时可以指定名称,
默认为Thread-1 Thread-2
Lock
多进程中,同一个变量每个进程都有一份拷贝互不影响,而在多线程中是共享进程中的变量(资源),因此会在执行时引发访问数据
时会出现错误等其他问题,为了避免这些问题引入了Lock锁的概念。
import time,threading
balance = 0
def change_it(n):
global balance
balance = balance + n
balance = balance - n
def run_thread(n):
for i in range(1000000):
change_it(n)
t1 = threading.Thread(target=run_thread,args=(5,),name='Change_it_Thread')
t2 = threading.Thread(target=run_thread,args=(8,),name='Change_it_Thread')
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)
理论上输出结果为0,但是这两个线程交替执行,只要循环次数足够多返回值就不一定是0了
这是因为高级语言的一条语句在翻译成机器指令时一般都会翻译成多个机器指令这会导致线程访问到的变量已经被其他
线程的执行改变了状态,造成数据的不一致。这时我们就需要一个Lock锁来解决问题,用来保证当线程A访问资源时线程B无法
访问该资源(比如一个变量)。
import time,threading
balance = 0
lock = threading.Lock()
def change_it(n):
global balance
balance = balance + n
balance = balance - n
def run_thread(n):
for i in range(1000000):
#在线程访问资源前上锁,保证在该线程访问资源结束前其他线程不能访问
lock.acquire()
try:
change_it(n)
finally:
#要有职业道德在访问完资源后一定要记得释放锁,不然其他线程无法访问该资源
lock.release()
t1 = threading.Thread(target=run_thread,args=(5,),name='Change_it_Thread')
t2 = threading.Thread(target=run_thread,args=(8,),name='Change_it_Thread')
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)
这样输出结果会衡为0,虽然会占用一些资源但是为了程序的正确运行是值得的。
Python的线程虽然是真正的线程,但解释器执行代码时,有一个GIL锁:Global Interpreter Lock,任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。
所以,在Python中,可以使用多线程,但不要指望能有效利用多核。如果一定要通过多线程利用多核,那只能通过C扩展来实现,不过这样就失去了Python简单易用的特点。
不过,也不用过于担心,Python虽然不能利用多线程实现多核任务,但可以通过多进程实现多核任务。多个Python进程有各自独立的GIL锁,互不影响。
Python解释器由于设计时有GIL全局锁,导致了多线程无法利用多核。多线程的并发在Python中就是一个美丽的梦。