Python教程

python多线程与多进程

本文主要是介绍python多线程与多进程,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

python多线程

python中有两种方式实现线程

1.实例化一个threading.Thread的对象,并传入一个初始化函数对象(initial function )作为线程执行的入口;
2.继承threading.Thread,并重写run函数

  • 方式1:创建threading.Thread对象
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....
  • 方式2:继承threading.Thread,并重写run
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

我们可以通过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

deamon守护线程

可以通过将创建的线程指定为守护线程(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多进程

python提供multiprocessing用于创建多进程

创建进程

创建进程的方式和创建线程的方式类似:

  1. 实例化一个multiprocessing.Process的对象,并传入一个初始化函数对象(initial function )作为新建进程执行入口;
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
  1. 继承multiprocessing.Process,并重写run函数;
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)
这篇关于python多线程与多进程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!