Python学习笔记16:标准库多线程(threading包裹)

Python主要是通过标准库threading包来实现多线程。

今天,互联网时代,所有的server您将收到大量请求。

server要利用多线程的方式的优势来处理这些请求,为了改善网络port读写效率。

Python它是一个网络server后台工作语言 (豆瓣网),所以多线程也就非常自然被Python语言支持。

多线程售票以及同步

我们使用Python来实现Linux多线程与同步文中的售票程序。

我们使用mutex (也就是Python中的Lock类对象) 来实现线程的同步:

import threading
import time
import os

# This function could be called by any function to do other chores.
def doChore():
    time.sleep(0.5)

# Function for each thread
def booth(tid):
    global i
    global lock
    while True:
        lock.acquire()                # Lock; or wait if other thread is holding the lock
        if i != 0:
            i = i - 1                 # Sell tickets
            print(tid,':now left:',i) # Tickets left
            doChore()                 # Other critical operations
        else:
            print("Thread_id",tid," No more tickets")
            os._exit(0)              # Exit the whole process immediately
        lock.release()               # Unblock
        doChore()                    # Non-critical operations

# Start of the main function
i    = 100                           # Available ticket number
lock = threading.Lock()              # Lock (i.e., mutex)

# Start 10 threads
for k in range(10):
    new_thread = threading.Thread(target=booth,args=(k,))   # Set up thread; target: the callable (function) to be run, args: the argument for the callable
    new_thread.start()                                      # run the thread

这里使用了两个全局变量,一个是i,用以储存剩余票数。一个是lock对象,用于同步线程对i的改动。

此外。在最后的for循环中,我们总共设置了10个线程。

每一个线程都运行booth()函数。

线程在调用start()方法的时候正式启动 (实际上,计算机中最多会有11个线程。由于主程序本身也会占用一个线程)。

Python使用threading.Thread对象来代表线程,用threading.Lock对象来代表一个相互排斥锁 (mutex)。

有两点须要注意:

我们在函数中使用global来声明变量为全局变量,从而让多线程共享i和lock (在C语言中,我们通过将变量放在全部函数外面来让它成为全局变量)。

假设不这么声明,因为i和lock是不可变数据对象。它们将被当作一个局部变量。

假设是可变数据对象的话,则不须要global声明。

我们甚至能够将可变数据对象作为參数来传递给线程函数。

这些线程将共享这些可变数据对象。我们在booth中使用了两个doChore()函数。

能够在未来改进程序。以便让线程除了进行i=i-1之外,做很多其它的操作,比方打印剩余票数,找钱,或者喝口水之类的。

第一个doChore()依旧在Lock内部。所以能够安全地使用共享资源 (critical operations, 比方打印剩余票数)。

第二个doChore()时。Lock已经被释放,所以不能再去使用共享资源。

这时候能够做一些不使用共享资源的操作 (non-critical operation, 比方找钱、喝水)。

这里有益让doChore()等待了0.5秒。以代表这些额外的操作可能花费的时间。

你能够定义的函数来取代doChore()。

OOP创建线程

上面的Python程序很类似于一个面向过程的C程序。

我们以下介绍怎样通过面向对象的方法实现多线程,其核心是继承threading.Thread类。

import threading
import time
import os

def doChore():
    time.sleep(0.5)

# Function for each thread
class BoothThread(threading.Thread):
    def __init__(self, tid, monitor):
        self.tid          = tid
        self.monitor = monitor
        threading.Thread.__init__(self)
    def run(self):
        while True:
            monitor['lock'].acquire()                          # Lock; or wait if other thread is holding the lock
            if monitor['tick'] != 0:
                monitor['tick'] = monitor['tick'] - 1          # Sell tickets
                print(self.tid,':now left:',monitor['tick'])   # Tickets left
                doChore()                                      # Other critical operations
            else:
                print("Thread_id",self.tid," No more tickets")
                os._exit(0)                                    # Exit the whole process immediately
            monitor['lock'].release()                          # Unblock
            doChore()                                          # Non-critical operations

# Start of the main function
monitor = {'tick':100, 'lock':threading.Lock()}

# Start 10 threads
for k in range(10):
    new_thread = BoothThread(k, monitor)
    new_thread.start()

上面的for循环中已经利用了threading.Thread()的方法来创建一个Thread对象,并将函数booth()以及其參数传递给改对象,并调用start()方法来执行线程。

OOP的话。通过改动Thread类的run()方法来定义线程所要运行的命令。

自定义了一个类BoothThread, 这个类继承自thread.Threading类。

然后我们把上面的booth()所进行的操作统统放入到BoothThread类的run()方法中。

注意。我们没有使用全局变量声明global,而是使用了一个词典monitor存放全局变量,然后把词典作为參数传递给线程函数。

因为词典是可变数据对象。所以当它被传递给函数的时候,函数所使用的依旧是同一个对象,相当于被多个线程所共享。

这也是多线程乃至于多进程编程的一个技巧 (应尽量避免上面的global声明的使用方法,由于它并不适用于windows平台)。

上面OOP编程方法与面向过程的编程方法相比,并没有带来太大实质性的区别。

其它

threading.Thread对象,我们已经介绍了该对象的start()和run(), 此外:

join()方法

调用该方法的线程将等待直到改Thread对象完毕。再恢复执行。这与进程间调用wait()函数相类似。

以下的对象用于处理多线程同步。

对象一旦被建立。能够被多个线程共享,并依据情况堵塞某些进程。

threading.Lock对象

mutex, 有acquire()和release()方法。

threading.Condition对象

condition variable,建立该对象时。会包括一个Lock对象 (由于condition variable总是和mutex一起使用)。

能够对Condition对象调用acquire()和release()方法,以控制潜在的Lock对象。

wait()方法

相当于cond_wait()

notify_all()

相当与cond_broadcast()

nofify()

与notify_all()功能类似,但仅仅唤醒一个等待的线程,而不是所有

threading.Semaphore对象

semaphore,也就是计数锁。

创建对象的时候。能够传递一个整数作为计数上限 (sema = threading.Semaphore(5))。

它与Lock类似,也有Lock的两个方法。

threading.Event对象

与threading.Condition相类似。相当于没有潜在的Lock保护的condition variable。

对象有True和False两个状态。

能够多个线程使用wait()等待。直到某个线程调用该对象的set()方法,将对象设置为True。

线程能够调用对象的clear()方法来重置对象为False状态。

# 最近这些章节并没有看得太清楚如何~~~~~

版权声明:本文博主原创文章,博客,未经同意不得转载。

时间: 2024-08-13 10:00:02

Python学习笔记16:标准库多线程(threading包裹)的相关文章

Python学习笔记16:标准库之多线程(threading包)

Python主要通过标准库中的threading包来实现多线程. 当今网络时代,每个服务器都会接收到大量的请求.服务器可以利用多线程的方式来处理这些请求,以提高对网络端口的读写效率. Python是一种网络服务器的后台工作语言 (比如豆瓣网),所以多线程也就很自然被Python语言支持. 多线程售票以及同步 我们使用Python来实现Linux多线程与同步文中的售票程序. 我们使用mutex (也就是Python中的Lock类对象) 来实现线程的同步: import threading impo

C++ Primer 学习笔记_9_标准库类型(续3) -- biteset

 标准库类型(四) --biteset 序言: 位是用来保存一组项或条件的yes/no信息[标识]的简洁方法. [cpp] view plaincopyprint? #include <bitset> using std::bitset; #include <bitset> using std::bitset; 正文: 1.bitset对象的定义和初始化 和vector对象不同的是:bitset类型对象的区别在于其长度而不是类型.在定义bitest时,要在尖括号中说明给出他的长

C++ Primer 学习笔记_7_标准库类型(续1) -- vector类型

 标准库类型(二) --vector类型 引子: vector是同一种类型的对象的集合,每个对象都有一个对应的整数索引值.和string对象一样,标准库将负责管理与存储元素相关的内存. 我们将vector称之为容器,一个容器中的所有对象都必须是同一类型的! [cpp] view plaincopyprint? #include <vector> using std::vector; #include <vector> using std::vector; [模板] vector

C++ Primer 学习笔记_8_标准库类型(续2) -- iterator

 标准库类型(三) --iterator 序言: 迭代器是一种检查容器内元素并遍历容器元素的数据类型. 所有的标准库容器都定义了相应的迭代器类型,而只有少数的容器支持下标操作:因此,现代C++更倾向于使用迭代器而不是下标操作访问容器元素. 正文: 1.容器的iterator类型 每个标准库容器类型都定义了一个名为iterator的成员: [cpp] view plaincopyprint? vector<int>::iterator iter; vector<int>::ite

C++ Primer 学习笔记_6_标准库类型 -- 命名空间using与string类型

 标准库类型(一) --命名空间using与string类型 引: 标准库类型是语言组成部分中更基本的哪些数据类型(如:数组.指针)的抽象! C++标准库定义的是高级的抽象数据类型: 1.高级:因为其中反映了更复杂的概念: 2.抽象:因为我们在使用时不需要关心他们是如何表示的,我们只需要知道这些抽象数据类型支持哪些操作就可以了. 正文: 一.命名空间的using声明 1. using std::cin; ::运算符的作用含义是右操作数的名字可以在左操作数的作用域中找到. 格式: [cpp]

Python学习笔记16—电子表格

openpyl 模块是解决 Microsoft Excel 2007/2010 之类版本中扩展名是 Excel 2010 xlsx/xlsm/xltx/xltm 的文件的读写的第三方库. 安装 pip install openpyxl workbook 和 sheet的建立 引入模块 >>> from openpyxl import Workbook 建立工作薄 >>> wb = Workbook() 建立sheet >>> ws = wb.activ

python基础教程_学习笔记16:标准库:一些最爱——random

标准库:一些最爱 random random模块包括返回随机数的函数,可以用于模拟或者用于任何产生随机输出的程序. 事实上,所产生的数字都是伪随机数,它们以一个可预测的系统作为基础,除非是为了强加密的目标,否则这些随机数还是足够随机的.如果真的需要随机性,可以使用os模块的urandom函数. 重要函数 函数 描述 random() 返回0<=n<1之间的随机实数n,其中0<n<=1 getrandbits(n) 以长整型形式返回n个随机位(二进制数) uniform(a,b) 返

python学习笔记-(十三)线程、进程、多线程&amp;多进程

为了方便大家理解下面的知识,可以先看一篇文章:http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html 线程 1.什么是线程? 线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务. 2.python GIL全局解释器锁(仅需了解) 无论你启多少个线程,你有多少个cpu, Python在执行的时

C++ Primer(第五版)学习笔记_5_标准模板库string(2)

C++ Primer(第五版)学习笔记_5_标准模板库string(2) 10.搜索string对象的元素或子串 采用find()方法可查找字符串中的第一个字符元素(char, 用单引号界定)或者子串(用双引号界定):如果查到,则返回下标值(从0开始计数),如果查不到,则返回一个很大的数string:npos(即:4294967295). #include <iostream> #include <stdio.h> #include <string> using nam