【问题标题】:Many threads to write log file at same time in Python在Python中同时写入日志文件的多个线程
【发布时间】:2014-10-12 14:01:51
【问题描述】:

我正在编写一个脚本以同时从多台计算机检索 WMI 信息,然后将此信息写入文本文件:

f = open("results.txt", 'w+') ## to clean the results file before the start


def filesize(asset):  
    f = open("results.txt", 'a+')  
    c = wmi.WMI(asset)  
    wql = 'SELECT FileSize,Name FROM CIM_DataFile where (Drive="D:" OR Drive="E:") and Caption like "%file%"'  
    for item in c.query(wql):  
        print >> f, item.Name.split("\\")[2].strip().upper(), str(item.FileSize)  




class myThread (threading.Thread):  
    def __init__(self,name):  
        threading.Thread.__init__(self)  
        self.name = name  
    def run(self):  
        pythoncom.CoInitialize ()  
        print "Starting " + self.name       
        filesize(self.name)  
        print "Exiting " + self.name  



thread1 = myThread('10.24.2.31')  
thread2 = myThread('10.24.2.32')  
thread3 = myThread('10.24.2.33')  
thread4 = myThread('10.24.2.34')  
thread1.start()  
thread2.start()  
thread3.start()  
thread4.start()

问题是所有线程同时写入。

【问题讨论】:

    标签: python multithreading locking python-2.x


    【解决方案1】:

    您可以简单地创建自己的锁定机制,以确保只有一个线程正在写入文件。

    import threading
    lock = threading.Lock()
    
    def write_to_file(f, text, file_size):
        lock.acquire() # thread blocks at this line until it can obtain lock
    
        # in this section, only one thread can be present at a time.
        print >> f, text, file_size
    
        lock.release()
    
    def filesize(asset):  
        f = open("results.txt", 'a+')  
        c = wmi.WMI(asset)  
        wql = 'SELECT FileSize,Name FROM CIM_DataFile where (Drive="D:" OR Drive="E:") and Caption like "%file%"'  
        for item in c.query(wql):  
            write_to_file(f, item.Name.split("\\")[2].strip().upper(), str(item.FileSize))
    

    您可能需要考虑在整个 for 循环 for item in c.query(wql): 周围放置锁,以允许每个线程在释放锁之前执行更大的工作块。

    【讨论】:

    • 如果一个线程在文件被锁定到另一个线程时尝试 write_to_file,是否仍然轮到他们写?
    • 是的,当拥有锁的线程释放它时,等待的线程将获得锁。
    • @MartinKonecny 我正在将这种获取和释放方法用于 python 脚本,其中 4 个线程同时写入一个文件。它整齐地排列任务并防止书写错误。我正在编写第二个(完全独立的脚本),它与第一个写入相同的文件。同时运行时,第一个脚本中实现的锁定和获取方法是否也阻止了第二个脚本(未实现的地方)同时访问?即此方法是否会为当时尝试访问它的每个脚本锁定文件? ...很好奇,但文档不清楚。
    • 不,这种锁定方法只适用于在同一脚本中运行的线程
    • 为什么有些人使用with lock : #operation
    【解决方案2】:

    print 不是线程安全的。请改用logging 模块(即):

    import logging
    import threading
    import time
    
    
    FORMAT = '[%(levelname)s] (%(threadName)-10s) %(message)s'
    
    logging.basicConfig(level=logging.DEBUG,
                        format=FORMAT)
    
    file_handler = logging.FileHandler('results.log')
    file_handler.setFormatter(logging.Formatter(FORMAT))
    logging.getLogger().addHandler(file_handler)
    
    
    def worker():
        logging.info('Starting')
        time.sleep(2)
        logging.info('Exiting')
    
    
    t1 = threading.Thread(target=worker)
    t2 = threading.Thread(target=worker)
    
    t1.start()
    t2.start()
    

    输出(和results.log的内容):

    [INFO] (Thread-1  ) Starting
    [INFO] (Thread-2  ) Starting
    [INFO] (Thread-1  ) Exiting
    [INFO] (Thread-2  ) Exiting
    

    您可以使用 name 关键字参数设置您自己的名称,而不是使用默认名称 (Thread-n),然后 %(threadName) 格式化指令将使用该参数:

    t = threading.Thread(name="My worker thread", target=worker)
    

    (这个例子改编自Doug Hellmann's excellent article about the threading module的一个例子)

    【讨论】:

      【解决方案3】:

      对于另一种解决方案,使用Pool 计算数据,将其返回给父进程。然后,该父级将所有数据写入文件。由于一次只有一个 proc 写入文件,因此无需额外锁定。

      注意以下使用进程池,而不是线程。这使得代码比使用threading 模块组合起来更简单、更容易。 (有一个ThreadPool 对象,但没有记录。)

      来源

      import glob, os, time
      from multiprocessing import Pool
      
      def filesize(path):
          time.sleep(0.1)
          return (path, os.path.getsize(path))
      
      paths = glob.glob('*.py')
      pool = Pool()                   # default: proc per CPU
      
      with open("results.txt", 'w+') as dataf:
          for (apath, asize) in pool.imap_unordered(
                  filesize, paths,
          ):
              print >>dataf, apath,asize
      

      在 results.txt 中输出

      zwrap.py 122
      usercustomize.py 38
      tpending.py 2345
      msimple4.py 385
      parse2.py 499
      

      【讨论】:

        猜你喜欢
        • 2018-07-29
        • 1970-01-01
        • 2019-07-27
        • 2022-11-11
        • 2012-01-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-01-27
        相关资源
        最近更新 更多