多线程-threading
python的thread模块是比较底层的模块,python的threading模块是对thread做了一些包装的,可以更加方便被使用
from threading import Threadimport timedef test(): print("---1---") time.sleep(1)for i in range(5): t = Thread(target=test) t.start()
输出
---1------1------1------1------1---
主线程等待子线程都执行完后才退出
使用thread子类创建线程
import threadingimport timeclass MyThread(threading.Thread): def run(self): msg = "I'm" + self.name + " @ " + str(i) # self.name是当前线程的名字 print(msg) time.sleep(1)for i in range(5): t = MyThread() t.start()
输出
I'm Thread-1 @ 0I'm Thread-2 @ 1I'm Thread-3 @ 2I'm Thread-4 @ 3I'm Thread-5 @ 4
线程的执行顺序
import threadingimport timeclass MyThread(threading.Thread): def run(self): for i in range(3): msg = "I'm " + self.name + " @ " + str(i) # self.name是当前线程的名字,也可以用threading.current_thread().name获取当前线程的名字 print(msg) time.sleep(1)for i in range(5): t = MyThread() t.start()
输出
I'm Thread-1 @ 0I'm Thread-2 @ 0I'm Thread-3 @ 0I'm Thread-4 @ 0I'm Thread-5 @ 0I'm Thread-1 @ 1I'm Thread-3 @ 1I'm Thread-2 @ 1I'm Thread-5 @ 1I'm Thread-4 @ 1I'm Thread-1 @ 2I'm Thread-2 @ 2I'm Thread-3 @ 2I'm Thread-5 @ 2I'm Thread-4 @ 2
线程共享全局变量
from threading import Threadimport timeg_num = 100def test1(): global g_num for i in range(3): g_num += 1 print("---in test1, g_num is %d---"%g_num)def test2(): global g_num print("---in test2, g_num is %d---" % g_num)print("---线程创建之前g_num is %d---"%g_num)t1 = Thread(target=test1)t1.start()time.sleep(1)t2 = Thread(target=test2)t2.start()
输出
---线程创建之前g_num is 100------in test1, g_num is 103------in test2, g_num is 103---
互斥锁
#创建锁
mutex = threading.Lock()#锁定mutex.acquire([blocking]) #释放mutex.release()
当多个线程要修改同一个全局变量时,要用互斥锁
g_num = 0def test1(): global g_num for i in range(10000): mutex.acquire() g_num += 1 mutex.release() print("--- test1 g_num=%d ---" % g_num)def test2(): global g_num for i in range(10000): mutex.acquire() g_num += 1 mutex.release() print("--- test2 g_num=%d ---" % g_num)mutex = Lock()t1 = Thread(target=test1)t1.start()t2 = Thread(target=test2)t2.start()print("--- g_num=%d ---"%g_num)
输出
---test1 g_num=10000------test2 g_num=20000---
注意:第一次上锁程序不会锁住,第二次在没有解锁的情况下上锁程序才会锁住。
总结:
1、线程共享全局变量。
2、线程不共享函数里面的代码。
3、非全局变量不需要加锁 。
4、加锁的代码越少越好。
5、主线程等待子线程都执行完后才退出。
ThreadLocal对象
⼀个ThreadLocal变量虽然是全局变量,但每个线程都只能读写⾃⼰线程的独 ⽴副本,互不⼲扰。ThreadLocal解决了参数在⼀个线程中各个函数之间互相传递的问题。
import threadinglocal_school = threading.local()def process_student(): # 获取当前线程关联的student: std = local_school.student print("hello, %s (in %s)"%(std, threading.current_thread().name))def process_thread(name): # 绑定ThreadLocal的student local_school.student = name process_student()t1 = threading.Thread(target=process_thread,args=("老李",), name="Thread-A")t2 = threading.Thread(target=process_thread,args=("老王",), name="Thread-B")t1.start()t2.start()#这里可以不加joint1.join()t2.join()
输出
hello, 老李 (in Thread-A)hello, 老王 (in Thread-B)
ThreadLocal最常⽤的地⽅就是为每个线程绑定⼀个数据库连接,HTTP请求,⽤户身份信息等,这样⼀个线程的所有调⽤到的处理函数都可以⾮常⽅便地访问这些资源。
异步
from multiprocessing import Poolimport timeimport osdef test(): print("---进程池中的进程--pid=%d,ppid=%d---"%(os.getpid(),os.getppid())) for i in range(3): print("---%d---"%i) time.sleep(1) return "hahah"def test2(args): print("---callback func--pid=%d"%os.getpid()) print("---callback func--args=%s"%args)if __name__ == '__main__': pool = Pool(3) pool.apply_async(func=test,callback=test2) while True: time.sleep(1) print("---主进程--pid=%d---"%os.getpid())
输出
---进程池中的进程--pid=9228,ppid=3520------0------主进程--pid=3520------1------主进程--pid=3520------2------主进程--pid=3520------callback func--pid=3520 #pid为主进程id,也就是说主进程放下手中的工作,开始执行test2函数,这就是异步。---callback func--args=hahah---主进程--pid=3520------主进程--pid=3520------主进程--pid=3520--- ...
GIL也就是全局解释器锁,GIL保证一个进程里,同一时间只有一个线程在执行任务,只在cpython解释器里有GIL,jpython解释器就没有。
python里的多线程实际上是假多线程。
python里的多进程是真的多进程。