Python 3 - 多线程编程

  • 简述

    运行多个线程类似于同时运行多个不同的程序,但具有以下好处 -
    • 一个进程中的多个线程与主线程共享相同的数据空间,因此与它们是单独的进程相比,它们可以更容易地共享信息或相互通信。
    • 线程有时被称为轻量级进程,它们不需要太多的内存开销;它们比流程便宜。
    一个线程有一个开始、一个执行顺序和一个结束。它有一个指令指针,可以跟踪它当前在上下文中的哪个位置运行。
    • 它可以被抢占(中断)。
    • 它可以在其他线程运行时暂时搁置(也称为休眠)——这称为让步。
    有两种不同的线程 -
    • 内核线程
    • 用户线程
    内核线程是操作系统的一部分,而用户空间线程并未在内核中实现。
    有两个模块支持在 Python3 中使用线程 -
    • _线
    • 穿线
    thread 模块已经“弃用”了很长时间。鼓励用户改用线程模块。因此,在 Python 3 中,“线程”模块不再可用。但是,为了在 Python3 中向后兼容,它已重命名为“_thread”。
  • 开始一个新线程

    要生成另一个线程,您需要调用线程模块中可用的以下方法 -
    
    _thread.start_new_thread ( function, args[, kwargs] )
    
    此方法调用支持在 Linux 和 Windows 中快速高效地创建新线程。
    方法调用立即返回,子线程启动并使用传递的args列表调用函数。当函数返回时,线程终止。
    在这里,args是一个参数元组;使用空元组调用函数而不传递任何参数。kwargs是关键字参数的可选字典。

    例子

    
    #!/usr/bin/python3
    import _thread
    import time
    # Define a function for the thread
    def print_time( threadName, delay):
       count = 0
       while count < 5:
          time.sleep(delay)
          count += 1
          print ("%s: %s" % ( threadName, time.ctime(time.time()) ))
    # Create two threads as follows
    try:
       _thread.start_new_thread( print_time, ("Thread-1", 2, ) )
       _thread.start_new_thread( print_time, ("Thread-2", 4, ) )
    except:
       print ("Error: unable to start thread")
    while 1:
       pass
    

    输出

    执行上述代码时,会产生以下结果 -
    
    Thread-1: Fri Feb 19 09:41:39 2016
    Thread-2: Fri Feb 19 09:41:41 2016
    Thread-1: Fri Feb 19 09:41:41 2016
    Thread-1: Fri Feb 19 09:41:43 2016
    Thread-2: Fri Feb 19 09:41:45 2016
    Thread-1: Fri Feb 19 09:41:45 2016
    Thread-1: Fri Feb 19 09:41:47 2016
    Thread-2: Fri Feb 19 09:41:49 2016
    Thread-2: Fri Feb 19 09:41:53 2016
    
    程序进入无限循环。您必须按 ctrl-c 才能停止
    虽然它对于低级线程非常有效,但与较新的线程模块相比,线程模块非常有限。
  • 线程模块

    Python 2.4 中包含的较新的线程模块为线程提供了比上一节中讨论的线程模块更强大、更高级的支持。
    threading模块公开了thread模块的所有方法并提供了一些额外的方法 -
    • threading.activeCount()− 返回活动线程对象的数量。
    • threading.currentThread()− 返回调用者线程控件中线程对象的数量。
    • threading.enumerate()− 返回当前活动的所有线程对象的列表。
    除了方法之外,threading 模块还有实现线程的Thread类。Thread类提供的方法如下:
    • run()− run() 方法是线程的入口点。
    • start()− start() 方法通过调用run 方法启动一个线程。
    • join([time])− join() 等待线程终止。
    • isMoove()− isMoove() 方法检查线程是否仍在执行。
    • getName()− getName() 方法返回线程的名称。
    • setName()− setName() 方法设置线程的名称。
  • 使用线程模块创建线程

    要使用线程模块实现一个新线程,您必须执行以下操作 -
    • 定义Thread类的新子类。
    • 覆盖__init__(self [,args])方法以添加额外的参数。
    • 然后,重写 run(self [,args]) 方法来实现线程在启动时应该做什么。
    一旦创建了新的Thread子类,就可以创建它的一个实例,然后通过调用start()启动一个新线程,而 start()又会调用run()方法。

    例子

    
    #!/usr/bin/python3
    import threading
    import time
    exitFlag = 0
    class myThread (threading.Thread):
       def __init__(self, threadID, name, counter):
          threading.Thread.__init__(self)
          self.threadID = threadID
          self.name = name
          self.counter = counter
       def run(self):
          print ("Starting " + self.name)
          print_time(self.name, self.counter, 5)
          print ("Exiting " + self.name)
    def print_time(threadName, delay, counter):
       while counter:
          if exitFlag:
             threadName.exit()
          time.sleep(delay)
          print ("%s: %s" % (threadName, time.ctime(time.time())))
          counter -= 1
    # Create new threads
    thread1 = myThread(1, "Thread-1", 1)
    thread2 = myThread(2, "Thread-2", 2)
    # Start new Threads
    thread1.start()
    thread2.start()
    thread1.join()
    thread2.join()
    print ("Exiting Main Thread")
    

    结果

    当我们运行上面的程序时,它会产生以下结果 -
    
    Starting Thread-1
    Starting Thread-2
    Thread-1: Fri Feb 19 10:00:21 2016
    Thread-2: Fri Feb 19 10:00:22 2016
    Thread-1: Fri Feb 19 10:00:22 2016
    Thread-1: Fri Feb 19 10:00:23 2016
    Thread-2: Fri Feb 19 10:00:24 2016
    Thread-1: Fri Feb 19 10:00:24 2016
    Thread-1: Fri Feb 19 10:00:25 2016
    Exiting Thread-1
    Thread-2: Fri Feb 19 10:00:26 2016
    Thread-2: Fri Feb 19 10:00:28 2016
    Thread-2: Fri Feb 19 10:00:30 2016
    Exiting Thread-2
    Exiting Main Thread
    
  • 同步线程

    Python 提供的线程模块包括一个易于实现的锁定机制,允许您同步线程。通过调用Lock()方法创建一个新锁,该方法返回新锁。
    新锁对象的acquire(blocking)方法用于强制线程同步运行。可选的阻塞参数使您能够控制线程是否等待获取锁。
    如果阻塞设置为 0,则如果无法获取锁,线程将立即返回 0 值,如果获取了锁,则返回 1。如果 blocking 设置为 1,则线程阻塞并等待锁被释放。
    新锁对象的release()方法用于在不再需要时释放锁。

    例子

    
    #!/usr/bin/python3
    import threading
    import time
    class myThread (threading.Thread):
       def __init__(self, threadID, name, counter):
          threading.Thread.__init__(self)
          self.threadID = threadID
          self.name = name
          self.counter = counter
       def run(self):
          print ("Starting " + self.name)
          # Get lock to synchronize threads
          threadLock.acquire()
          print_time(self.name, self.counter, 3)
          # Free lock to release next thread
          threadLock.release()
    def print_time(threadName, delay, counter):
       while counter:
          time.sleep(delay)
          print ("%s: %s" % (threadName, time.ctime(time.time())))
          counter -= 1
    threadLock = threading.Lock()
    threads = []
    # Create new threads
    thread1 = myThread(1, "Thread-1", 1)
    thread2 = myThread(2, "Thread-2", 2)
    # Start new Threads
    thread1.start()
    thread2.start()
    # Add threads to thread list
    threads.append(thread1)
    threads.append(thread2)
    # Wait for all threads to complete
    for t in threads:
       t.join()
    print ("Exiting Main Thread")
    

    输出

    执行上述代码时,会产生以下结果 -
    
    Starting Thread-1
    Starting Thread-2
    Thread-1: Fri Feb 19 10:04:14 2016
    Thread-1: Fri Feb 19 10:04:15 2016
    Thread-1: Fri Feb 19 10:04:16 2016
    Thread-2: Fri Feb 19 10:04:18 2016
    Thread-2: Fri Feb 19 10:04:20 2016
    Thread-2: Fri Feb 19 10:04:22 2016
    Exiting Main Thread
    
  • 多线程优先级队列

    Queue模块允许您创建一个可以容纳特定数量项目的新队列对象。有以下方法来控制队列 -
    • get()− get() 从队列中删除并返回一个项目。
    • put()− put 将项目添加到队列中。
    • qsize() − qsize() 返回队列中当前的项目数。
    • empty()− 如果队列为空,则 empty() 返回 True;否则,假。
    • full()− 如果队列已满,则 full() 返回 True;否则,假。

    例子

    
    #!/usr/bin/python3
    import queue
    import threading
    import time
    exitFlag = 0
    class myThread (threading.Thread):
       def __init__(self, threadID, name, q):
          threading.Thread.__init__(self)
          self.threadID = threadID
          self.name = name
          self.q = q
       def run(self):
          print ("Starting " + self.name)
          process_data(self.name, self.q)
          print ("Exiting " + self.name)
    def process_data(threadName, q):
       while not exitFlag:
          queueLock.acquire()
          if not workQueue.empty():
             data = q.get()
             queueLock.release()
             print ("%s processing %s" % (threadName, data))
          else:
             queueLock.release()
             time.sleep(1)
    threadList = ["Thread-1", "Thread-2", "Thread-3"]
    nameList = ["One", "Two", "Three", "Four", "Five"]
    queueLock = threading.Lock()
    workQueue = queue.Queue(10)
    threads = []
    threadID = 1
    # Create new threads
    for tName in threadList:
       thread = myThread(threadID, tName, workQueue)
       thread.start()
       threads.append(thread)
       threadID += 1
    # Fill the queue
    queueLock.acquire()
    for word in nameList:
       workQueue.put(word)
    queueLock.release()
    # Wait for queue to empty
    while not workQueue.empty():
       pass
    # Notify threads it's time to exit
    exitFlag = 1
    # Wait for all threads to complete
    for t in threads:
       t.join()
    print ("Exiting Main Thread")
    

    输出

    执行上述代码时,会产生以下结果 -
    
    Starting Thread-1
    Starting Thread-2
    Starting Thread-3
    Thread-1 processing One
    Thread-2 processing Two
    Thread-3 processing Three
    Thread-1 processing Four
    Thread-2 processing Five
    Exiting Thread-3
    Exiting Thread-1
    Exiting Thread-2
    Exiting Main Thread