如何在Python中分析多线程程序?

时间:2009-03-17 08:45:25

标签: python multithreading profiling

我正在使用Python开发一个固有的多线程模块,我想知道它在哪里花费时间。 cProfile似乎只是描述主线程。有没有办法分析计算中涉及的所有线程?

7 个答案:

答案 0 :(得分:35)

请参阅yappi(又一个Python Profiler)。

答案 1 :(得分:17)

您可以在每个线程中运行单独的cProfile实例,然后合并统计信息,而不是运行一个cProfileStats.add()会自动执行此操作。

答案 2 :(得分:7)

如果你可以做一些额外的工作,你可以编写自己的分析类来实现profile(self, frame, event, arg)。无论何时调用函数都会调用它,并且您可以相当容易地设置结构以从中收集统计信息。

然后,您可以使用threading.setprofile在每个线程上注册该函数。调用该函数时,您可以使用threading.currentThread()查看它正在运行的函数。更多信息(和准备运行的食谱):

http://code.activestate.com/recipes/465831/

http://docs.python.org/library/threading.html#threading.setprofile

答案 3 :(得分:2)

鉴于您的不同主题的主要功能不同,您可以使用here中非常有用的profile_func()装饰器。

答案 4 :(得分:1)

从Dask项目中检出mtprof

https://github.com/dask/mtprof

这是cProfile的直接替代品,如果您的线程以通常的方式启动并在主线程之前完成,则会将其统计信息汇总到相同的报告统计信息中。对我来说就像一个魅力。

答案 5 :(得分:0)

从2019年开始:我喜欢vartec的建议,但真的会喜欢一个示例代码。因此,我建立了一个-实施起来并不疯狂,但是您确实需要考虑一些事项。这是一个工作示例(Python 3.6):

您可以看到结果考虑了Thread1和thread2调用thread_func()所花费的时间。

您需要在代码中进行的唯一更改是子类threading.Thread,重写其run()方法。最小的更改,以便轻松地分析线程。

import threading
import cProfile
from time import sleep
from pstats import Stats
import pstats
from time import time
import threading
import sys

# using different times to ensure the results reflect all threads
SHORT = 0.5
MED = 0.715874
T1_SLEEP = 1.37897
T2_SLEEP = 2.05746
ITER = 1
ITER_T = 4

class MyThreading(threading.Thread):
    """ Subclass to arrange for the profiler to run in the thread """
    def run(self):
        """ Here we simply wrap the call to self._target (the callable passed as arg to MyThreading(target=....) so that cProfile runs it for us, and thus is able to profile it. 
            Since we're in the current instance of each threading object at this point, we can run arbitrary number of threads & profile all of them 
        """
        try:
            if self._target:
                # using the name attr. of our thread to ensure unique profile filenames
                cProfile.runctx('self._target(*self._args, **self._kwargs)', globals=globals(), locals=locals(), filename= f'full_server_thread_{self.name}')
        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

def main(args):
    """ Main func. """
    thread1_done =threading.Event()
    thread1_done.clear()
    thread2_done =threading.Event()
    thread2_done.clear()

    print("Main thread start.... ")
    t1 = MyThreading(target=thread_1, args=(thread1_done,), name="T1" )
    t2 = MyThreading(target=thread_2, args=(thread2_done,), name="T2" )
    print("Subthreads instances.... launching.")

    t1.start()          # start will call our overrident threading.run() method
    t2.start()

    for i in range(0,ITER):
        print(f"MAIN iteration: {i}")
        main_func_SHORT()
        main_func_MED()

    if thread1_done.wait() and thread2_done.wait():
        print("Threads are done now... ")
        return True

def main_func_SHORT():
    """ Func. called by the main T """
    sleep(SHORT)
    return True

def main_func_MED():
    sleep(MED)
    return True


def thread_1(done_flag):
    print("subthread target func 1 ")
    for i in range(0,ITER_T):
        thread_func(T1_SLEEP)
    done_flag.set()

def thread_func(SLEEP):
    print(f"Thread func")
    sleep(SLEEP)

def thread_2(done_flag):
    print("subthread target func 2 ")
    for i in range(0,ITER_T):
        thread_func(T2_SLEEP)
    done_flag.set()

if __name__ == '__main__':

    import sys
    args = sys.argv[1:]
    cProfile.run('main(args)', f'full_server_profile')
    stats = Stats('full_server_profile')
    stats.add('full_server_thread_T1')
    stats.add('full_server_thread_T2')
    stats.sort_stats('filename').print_stats()

答案 6 :(得分:-2)

我不知道任何为python支持此类事情的分析应用程序 - 但您可以编写一个Trace-class来编写日志文件,您可以在其中输入操作何时开始以及何时结束以及如何消耗的时间很长。

这是一个简单快速的解决方案。