1.实例化一个threading.Thread的对象,并传入一个初始化函数对象(initial function )作为线程执行的入口;
2.继承threading.Thread,并重写run函数
from threading import Thread import time def tstart(arg): time.sleep(0.5) print("%s running...." % arg) if __name__ == '__main__': # 创建线程对象,(参数1:target 固定格式跟函数名称,不加括号 参数2:args是一个元组,) t1 = Thread(target=tstart, args=('This is thread 1',)) t2 = Thread(target=tstart, args=('This is thread 2',)) t1.start() t2.start() print("This is main function")
结果:
This is main function This is thread 2 running.... This is thread 1 running....
import threading import time class CustomThread(threading.Thread): def __init__(self, thread_name): # step 1: call base __init__ function super(CustomThread, self).__init__(name=thread_name) self._tname = thread_name def run(self): # step 2: overide run function time.sleep(0.5) print("This is %s running...." % self._tname) if __name__ == "__main__": t1 = CustomThread("thread 1") t2 = CustomThread("thread 2") t1.start() t2.start() print("This is main function")
执行结果同方式1.
上面两种方法本质上都是直接或者间接使用Thread类,导入Thread类的方式不同,有的直接导入,有的间接导入
关联上面两种创建线程的方式:
import threading import time class CustomThread(threading.Thread): def __init__(self, thread_name, target = None): # step 1: call base __init__ function super(CustomThread, self).__init__(name=thread_name, target=target, args = (thread_name,)) self._tname = thread_name def run(self): # step 2: overide run function # time.sleep(0.5) # print("This is %s running....@run" % self._tname) super(CustomThread, self).run() def target(arg): time.sleep(0.5) print("This is %s running....@target" % arg) if __name__ == "__main__": t1 = CustomThread("thread 1", target) t2 = CustomThread("thread 2", target) t1.start() t2.start() print("This is main function")
结果:
This is main function This is thread 1 running....@target This is thread 2 running....@target
上面这段代码说明:
两种方式创建线程,指定的参数最终都会传给threading.Thread类;
传给线程的目标函数是在基类Thread的run函数体中被调用的,如果run没有被重写的话。
threading模块的一些属性和方法可以参照官网,这里重点介绍一下threading.Thread对象的方法
下面是threading.Thread提供的线程对象方法和属性
- start():创建线程后通过start启动线程,等待CPU调度,为run函数执行做准备;
- run():线程开始执行的入口函数,函数体中会调用用户编写的target函数,或者执行被重载的run函数;
- join([timeout]):阻塞挂起调用该函数的线程,直到被调用线程执行完成或超时。通常会在主线程中调用该方法,等待其他线程执行完成。
- name、getName()&setName():线程名称相关的操作;
- ident:整数类型的线程标识符,线程开始执行前(调用start之前)为None;
- isAlive()、is_alive():start函数执行之后到run函数执行完之前都为True;
- daemon、isDaemon()&setDaemon():守护线程相关;>
在主线程中创建若线程之后,他们之间没有任何协作和同步,除主线程之外每个线程都是从run开始被执行,直到执行完毕。
我们可以通过join方法让主线程阻塞,等待其创建的线程执行完成。
import threading import time def tstart(arg): print("%s running....at: %s" % (arg,time.time())) time.sleep(1) print("%s is finished! at: %s" % (arg,time.time())) if __name__ == '__main__': t1 = threading.Thread(target=tstart, args=('This is thread 1',)) t1.start() t1.join() # 当前线程阻塞,等待t1线程执行完成 print("This is main function at:%s" % time.time())
结果:
This is thread 1 running....at: 1564906617.43 This is thread 1 is finished! at: 1564906618.43 This is main function at:1564906618.43
可以通过将创建的线程指定为守护线程(daemon),这样主线程执行完毕之后会立即结束未执行完的线程,然后结束程序。
import threading import time def tstart(arg): print("%s running....at: %s" % (arg,time.time())) time.sleep(1) print("%s is finished! at: %s" % (arg,time.time())) if __name__ == '__main__': t1 = threading.Thread(target=tstart, args=('This is thread 1',)) t1.setDaemon(True) t1.start() # t1.join() # 当前线程阻塞,等待t1线程执行完成 print("This is main function at:%s" % time.time())
结果:
This is thread 1 running....at: 1564906847.85 This is main function at:1564906847.85
python提供multiprocessing用于创建多进程
创建进程的方式和创建线程的方式类似:
from multiprocessing import Process import os, time def pstart(name): # time.sleep(0.1) print("Process name: %s, pid: %s "%(name, os.getpid())) if __name__ == "__main__": subproc = Process(target=pstart, args=('subprocess',)) subproc.start() subproc.join() print("subprocess pid: %s"%subproc.pid) print("current process pid: %s" % os.getpid())
结果:
Process name: subprocess, pid: 4888 subprocess pid: 4888 current process pid: 9912
from multiprocessing import Process import os, time class CustomProcess(Process): def __init__(self, p_name, target=None): # step 1: call base __init__ function() super(CustomProcess, self).__init__(name=p_name, target=target, args=(p_name,)) def run(self): # step 2: # time.sleep(0.1) print("Custom Process name: %s, pid: %s "%(self.name, os.getpid())) if __name__ == '__main__': p1 = CustomProcess("process_1") p1.start() p1.join() print("subprocess pid: %s"%p1.pid) print("current process pid: %s" % os.getpid())
注意 由于每一个进程拥有独立的内存地址空间且互相隔离,因此不同进程看到的share_data是不同的、分别位于不同的地址空间,同时访问不会有问题。
两个进程必须使用同一个队列. 否则数据传输不了
from multiprocessing import Process,Queue #注意:这里导入的队列是进程模块下的 # 创建队列对象 q = Queue() p1 = Process(target=get_img_src, args=(q,)) p2 = Process(target=download_img, args=(q,)) p1.start() p2.start() # 比如第一个进程是获取url 路径,那么在get_img_src()函数中我们就可以把获取到的url的值存入队列中 q.put(xxxx) ,那么取值为q.get(),这样我们就可以实现进程中数据的共享了
from concurrent.futures import ThreadPoolExecutor def fn(name): for i in range(1000): print(name, i) if __name__ == '__main__': with ThreadPoolExecutor(10) as t: for i in range(100): t.submit(fn, name=f"线程{i}")
如果任务有返回值怎么办?
def func(name): time.sleep(2) return name def do_callback(res): print(res.result()) if __name__ == '__main__': with ThreadPoolExecutor(10) as t: names = ["线程1", "线程2", "线程3"] for name in names: # 方案一, 添加回调 t.submit(func, name).add_done_callback(do_callback) if __name__ == '__main__': start = time.time() with ThreadPoolExecutor(10) as t: names = [5, 2, 3] # 方案二, 直接用map进行任务分发. 最后统一返回结果 results = t.map(func, names) # 结果是按照你传递的顺序来执行的, 代价就是如果第一个没结束. 后面就都没结果 for r in results: print("result", r) print(time.time() - start)