python多线程简单操作

python的线程类似于java 的线程,也区分守护线程和非守护线程。守护线程的作用就是为其他线程的运行提供便利。

默认是非守护线程。当进程所有的非守护结束后,进程会自动结束。

1. 线程简单使用

1. 直接new Thread 的方式

# 线程使用的方式一
import threading

# 需要多线程运行的函数
import time


def doPrint(param):
    current = time.strftime("%Y%m%d%H%M%S", time.localtime(time.time()))
    print(current + "\t" + str(param))


def fun(args):
    doPrint("线程开始, name: {}, args: {}".format(threading.current_thread().getName(), args))
    doPrint("thread is Daemon: " + str(threading.current_thread().isDaemon()))
    time.sleep(3)
    doPrint("线程结束, name: {}, args: {}".format(threading.current_thread().getName(), args))


if __name__ == '__main__':
    # 打印当前main 线程的信息
    doPrint(threading.current_thread().getName())
    doPrint(threading.current_thread().__class__)

    # 创建一个线程并且开始一个新的线程。 两个参数就可以。
    t1 = threading.Thread(target=fun, args=(1,))
    # t1 = threading.Thread(target=fun, name='t1', args=(1,), daemon=True)
    t1.start()

    time.sleep(1)
    doPrint("main thread end ")

结果: (可以看出默认的线程规则是 Thread-{num})

20220729174638	MainThread
20220729174638	<class 'threading._MainThread'>
20220729174638	线程开始, name: Thread-1, args: 1
20220729174638	thread is Daemon: False
20220729174639	main thread end 
20220729174641	线程结束, name: Thread-1, args: 1

改成第二种指定name,并且设置为守护线程查看日志如下:

20220729174843	MainThread
20220729174843	<class 'threading._MainThread'>
20220729174843	线程开始, name: t1, args: 1
20220729174843	thread is Daemon: True
20220729174844	main thread end 

2. 继承的方式

import threading
import time


def doPrint(param):
    current = time.strftime("%Y%m%d%H%M%S", time.localtime(time.time()))
    print(current + "\t" + str(param))


class MyThreading(threading.Thread):
    def __init__(self, name):
        super(MyThreading, self).__init__()
        self.name = name

    # 线程要运行的代码
    def run(self):
        doPrint("线程开始, name: {}".format(threading.current_thread().getName()))
        doPrint("thread is Daemon: " + str(threading.current_thread().isDaemon()))
        time.sleep(3)
        doPrint("线程结束, name: {}".format(threading.current_thread().getName()))


if __name__ == '__main__':
    # 打印当前main 线程的信息
    doPrint(threading.current_thread().getName())
    doPrint(threading.current_thread().__class__)

    # 创建一个线程并且开始一个新的线程。 两个参数就可以。
    # t1 = threading.Thread(target=fun, args=(1,))
    t1 = MyThreading("cus-1")
    t1.start()

    time.sleep(1)
    doPrint("main thread end ")

结果:

20220729175358	MainThread
20220729175358	<class 'threading._MainThread'>
20220729175358	线程开始, name: cus-1
20220729175358	thread is Daemon: False
20220729175359	main thread end 
20220729175401	线程结束, name: cus-1

3. 源码查看

  1. Thread 构造方法 threading.Thread.init
    def __init__(self, group=None, target=None, name=None,
                 args=(), kwargs=None, *, daemon=None):
        """This constructor should always be called with keyword arguments. Arguments are:

        *group* should be None; reserved for future extension when a ThreadGroup
        class is implemented.

        *target* is the callable object to be invoked by the run()
        method. Defaults to None, meaning nothing is called.

        *name* is the thread name. By default, a unique name is constructed of
        the form "Thread-N" where N is a small decimal number.

        *args* is the argument tuple for the target invocation. Defaults to ().

        *kwargs* is a dictionary of keyword arguments for the target
        invocation. Defaults to {}.

        If a subclass overrides the constructor, it must make sure to invoke
        the base class constructor (Thread.__init__()) before doing anything
        else to the thread.

        """
        assert group is None, "group argument must be None for now"
        if kwargs is None:
            kwargs = {}
        self._target = target
        self._name = str(name or _newname())
        self._args = args
        self._kwargs = kwargs
        if daemon is not None:
            self._daemonic = daemon
        else:
            self._daemonic = current_thread().daemon
        self._ident = None
        if _HAVE_THREAD_NATIVE_ID:
            self._native_id = None
        self._tstate_lock = None
        self._started = Event()
        self._is_stopped = False
        self._initialized = True
        # Copy of sys.stderr used by self._invoke_excepthook()
        self._stderr = _sys.stderr
        self._invoke_excepthook = _make_invoke_excepthook()
        # For debugging and _after_fork()
        _dangling.add(self)

threading.Thread.run 线程默认的run 方法:

    def run(self):
        """Method representing the thread's activity.

        You may override this method in a subclass. The standard run() method
        invokes the callable object passed to the object's constructor as the
        target argument, if any, with sequential and keyword arguments taken
        from the args and kwargs arguments, respectively.

        """
        try:
            if self._target:
                self._target(*self._args, **self._kwargs)
        finally:
            # Avoid a refcycle if the thread is running a function with
            # an argument that has a member that points to the thread.
            del self._target, self._args, self._kwargs

​ 可以看出, 如果传了target 参数,会调用方法,传入的参数就是自身的两个属性 _args(tuple 元组类型) 和 _kwargs(dict类型)

2. 线程相关方法

1. join 变成同步

# 线程使用的方式一
import threading

# 需要多线程运行的函数
import time


def doPrint(param):
    current = time.strftime("%Y%m%d%H%M%S", time.localtime(time.time()))
    print(current + "\t" + str(param))


def fun(args):
    doPrint("线程开始, name: {}, args: {}".format(threading.current_thread().getName(), args))
    doPrint("thread is Daemon: " + str(threading.current_thread().isDaemon()))
    time.sleep(3)
    doPrint("线程结束, name: {}, args: {}".format(threading.current_thread().getName(), args))


if __name__ == '__main__':
    # 打印当前main 线程的信息
    doPrint(threading.current_thread().getName())
    doPrint(threading.current_thread().__class__)

    # 创建一个线程并且开始一个新的线程。 两个参数就可以。
    t1 = threading.Thread(target=fun, args=(1,))
    t1.start()
    # join 方法相当于当前线程等待join 的方法完成,类似于同步的效果。 可以传一个timeout  参数,指定超时时间
    t1.join()

    time.sleep(1)
    doPrint("main thread end ")

结果:

20220729180843	MainThread
20220729180843	<class 'threading._MainThread'>
20220729180843	线程开始, name: Thread-1, args: 1
20220729180843	thread is Daemon: False
20220729180846	线程结束, name: Thread-1, args: 1
20220729180847	main thread end 

2. 线程池使用

import random
import threading
import time
from concurrent.futures import ThreadPoolExecutor


def doPrint(param):
    current = time.strftime("%Y%m%d%H%M%S", time.localtime(time.time()))
    print(current + "\t" + threading.current_thread().getName() + "\t" + str(param))


def task(video_url):
    doPrint("开始执行任务" + video_url)
    time.sleep(1)
    # 将结果封装成一个Futuer对象,返回给线程池
    return random.randint(0, 10)


# response就是futuer对象,也就是task的返回值分装的一个Futuer对象
def done(response):
    # 即Futuer.result():取出task的返回值
    doPrint("任务执行完后,回调的函数" + str(response.result()))


# 创建线程池
threadpool = ThreadPoolExecutor(10)
url_list = ["www.xxxx-{}.com".format(i) for i in range(5)]
for url in url_list:
    # futuer是由task返回的一个Future对象,里面有记录task的返回值
    futuer = threadpool.submit(task, url)
    # 回调done函数,执行者依然是子线程
    futuer.add_done_callback(done)

doPrint("等待线程池中的任务执行完毕中······")
threadpool.shutdown(True)  # 等待线程池中的任务执行完毕后,在继续执行
doPrint("END")

结果:

20220729205033	ThreadPoolExecutor-0_0	开始执行任务www.xxxx-0.com
20220729205033	ThreadPoolExecutor-0_1	开始执行任务www.xxxx-1.com
20220729205033	ThreadPoolExecutor-0_2	开始执行任务www.xxxx-2.com
20220729205033	ThreadPoolExecutor-0_3	开始执行任务www.xxxx-3.com
20220729205033	ThreadPoolExecutor-0_4	开始执行任务www.xxxx-4.com20220729205033	MainThread	等待线程池中的任务执行完毕中······

20220729205034	ThreadPoolExecutor-0_0	任务执行完后,回调的函数3
20220729205034	ThreadPoolExecutor-0_1	任务执行完后,回调的函数9
20220729205034	ThreadPoolExecutor-0_4	任务执行完后,回调的函数6
20220729205034	ThreadPoolExecutor-0_2	任务执行完后,回调的函数3
20220729205034	ThreadPoolExecutor-0_3	任务执行完后,回调的函数3
20220729205034	MainThread	END
posted @ 2022-07-30 11:16  QiaoZhi  阅读(154)  评论(0编辑  收藏  举报