Python3标准库:threading进程中管理并发操作

1. threading进程中管理并发操作

threading模块提供了管理多个线程执行的API,允许程序在同一个进程空间并发的运行多个操作。

1.1 Thread对象

要使用Thread,最简单的方法就是用一个目标函数实例化一个Thread对象,并调用start()让它开始工作。

import threading

def worker():
    """thread worker function"""
    print(‘Worker‘)

threads = []
for i in range(5):
    t = threading.Thread(target=worker)
    threads.append(t)
    t.start()

输出有5行,每一行都是"Worker"。

如果能够创建一个线程,并向它传递参数告诉它要完成什么工作,那么这会很有用。任何类型的对象都可以作为参数传递到线程。下面的例子传递了一个数,线程将打印出这个数。

import threading

def worker(num):
    """thread worker function"""
    print(‘Worker: %s‘ % num)

threads = []
for i in range(5):
    t = threading.Thread(target=worker, args=(i,))
    threads.append(t)
    t.start()

现在这个整数参数会包含在各线程打印的消息中。

1.2 确定当前线程

使用参数来标识或命名线程很麻烦,也没有必要。每个Thread实例都有一个带有默认值的名,该默认值可以在创建线程时改变。如果服务器进程中有多个服务线程处理不同的操作,那么在这样的服务器进程中,对线程命名就很有用。

import threading
import time

def worker():
    print(threading.current_thread().getName(), ‘Starting‘)
    time.sleep(0.2)
    print(threading.current_thread().getName(), ‘Exiting‘)

def my_service():
    print(threading.current_thread().getName(), ‘Starting‘)
    time.sleep(0.3)
    print(threading.current_thread().getName(), ‘Exiting‘)

t = threading.Thread(name=‘my_service‘, target=my_service)
w = threading.Thread(name=‘worker‘, target=worker)
w2 = threading.Thread(target=worker)  # use default name

w.start()
w2.start()
t.start()

调试输出的每一行中包含有当前线程的名。线程名列中有"Thread-1"的行对应未命名的线程w2。

大多数程序并不使用print来进行调试。logging模块支持将线程名嵌入到各个日志消息中(使用格式化代码%(threadName)s)。通过把线程名包含在日志消息中,就能跟踪这些消息的来源。

import logging
import threading
import time

def worker():
    logging.debug(‘Starting‘)
    time.sleep(0.2)
    logging.debug(‘Exiting‘)

def my_service():
    logging.debug(‘Starting‘)
    time.sleep(0.3)
    logging.debug(‘Exiting‘)

logging.basicConfig(
    level=logging.DEBUG,
    format=‘[%(levelname)s] (%(threadName)-10s) %(message)s‘,
)

t = threading.Thread(name=‘my_service‘, target=my_service)
w = threading.Thread(name=‘worker‘, target=worker)
w2 = threading.Thread(target=worker)  # use default name

w.start()
w2.start()
t.start()

而且logging是线程安全的,所以来自不同线程的消息在输出中会有所区分。

1.3 守护与非守护线程

到目前为止,示例程序都在隐式地等待所有线程完成工作之后才退出。不过,程序有
时会创建一个线程作为守护线程(daemon),这个线程可以一直运行而不阻塞主程序退出。
如果一个服务不能很容易地中断线程,或者即使让线程工作到一半时中止也不会造成数据
损失或破坏(例如,为一个服务监控工具生成“心跳”的线程),那么对于这些服务,使用
守护线程就很有用。要标志一个线程为守护线程,构造线程时便要传入daemon=True或
者要调用它的setDaemon()方法并提供参数True。默认情况下线程不作为守护线程。

import threading
import time
import logging

def daemon():
    logging.debug(‘Starting‘)
    time.sleep(0.2)
    logging.debug(‘Exiting‘)

def non_daemon():
    logging.debug(‘Starting‘)
    logging.debug(‘Exiting‘)

logging.basicConfig(
    level=logging.DEBUG,
    format=‘(%(threadName)-10s) %(message)s‘,
)

d = threading.Thread(name=‘daemon‘, target=daemon, daemon=True)

t = threading.Thread(name=‘non-daemon‘, target=non_daemon)

d.start()
t.start()

这个代码的输出中不包含守护线程的“Exiting“消息,因为在从sleep()调用唤醒
守护线程之前,所有非守护线程(包括主线程)已经退出。

要等待一个守护线程完成工作,需要使用join()方法。

import threading
import time
import logging

def daemon():
    logging.debug(‘Starting‘)
    time.sleep(0.2)
    logging.debug(‘Exiting‘)

def non_daemon():
    logging.debug(‘Starting‘)
    logging.debug(‘Exiting‘)

logging.basicConfig(
    level=logging.DEBUG,
    format=‘(%(threadName)-10s) %(message)s‘,
)

d = threading.Thread(name=‘daemon‘, target=daemon, daemon=True)

t = threading.Thread(name=‘non-daemon‘, target=non_daemon)

d.start()
t.start()

d.join()
t.join()

使用join()等待守护线程退出意味着它有机会生成它的"Exiting"消息。

默认地,join()会无限阻塞。或者,还可以传入一个浮点值,表示等待线程在多长
时间(秒数)后变为不活动。即使线程在这个时间段内未完成,join()也会返回。

import threading
import time
import logging

def daemon():
    logging.debug(‘Starting‘)
    time.sleep(0.2)
    logging.debug(‘Exiting‘)

def non_daemon():
    logging.debug(‘Starting‘)
    logging.debug(‘Exiting‘)

logging.basicConfig(
    level=logging.DEBUG,
    format=‘(%(threadName)-10s) %(message)s‘,
)

d = threading.Thread(name=‘daemon‘, target=daemon, daemon=True)

t = threading.Thread(name=‘non-daemon‘, target=non_daemon)

d.start()
t.start()

d.join(0.1)
print(‘d.isAlive()‘, d.isAlive())
t.join()

由于传人的超时时间小于守护线程睡眠的时间,所以join()返回之后这个线程仍是"活着"。

1.4 枚举所有线程

没有必要为所有守护线程维护一个显示句柄来确保它们在退出主进程之前已经完成。

enumerate()会返回活动 Thread实例的一个列表。这个列表也包括当前线程,由于等
待当前线程终止(join)会引入一种死锁情况,所以必须跳过。

import random
import threading
import time
import logging

def worker():
    """thread worker function"""
    pause = random.randint(1, 5) / 10
    logging.debug(‘sleeping %0.2f‘, pause)
    time.sleep(pause)
    logging.debug(‘ending‘)

logging.basicConfig(
    level=logging.DEBUG,
    format=‘(%(threadName)-10s) %(message)s‘,
)

for i in range(3):
    t = threading.Thread(target=worker, daemon=True)
    t.start()

main_thread = threading.main_thread()
for t in threading.enumerate():
    if t is main_thread:
        continue
    logging.debug(‘joining %s‘, t.getName())
    t.join()

由于工作线程睡眠的时间量是随机的,所以这个程序的输出可能有变化。

1.5 派生线程

开始时,Thread要完成一些基本初始化,然后调用其run()方法,这会调用传递到构造函数的目标函数。要创建Thread的一个子类,需要覆盖run()来完成所需的工作。

import threading
import logging

class MyThread(threading.Thread):

    def run(self):
        logging.debug(‘running‘)

logging.basicConfig(
    level=logging.DEBUG,
    format=‘(%(threadName)-10s) %(message)s‘,
)

for i in range(5):
    t = MyThread()
    t.start()

run()的返回值将被忽略。

由于传递到Thread构造函数的args和kwargs值保存在私有变量中(这些变量名都有前缀),所以不能很容易地从子类访问这些值。要向一个定制的线程类型传递参数,需要重新定义构造函数,将这些值保存在子类可见的一个实例属性中。

import threading
import logging

class MyThreadWithArgs(threading.Thread):

    def __init__(self, group=None, target=None, name=None,
                 args=(), kwargs=None, *, daemon=None):
        super().__init__(group=group, target=target, name=name,
                         daemon=daemon)
        self.args = args
        self.kwargs = kwargs

    def run(self):
        logging.debug(‘running with %s and %s‘,
                      self.args, self.kwargs)

logging.basicConfig(
    level=logging.DEBUG,
    format=‘(%(threadName)-10s) %(message)s‘,
)

for i in range(5):
    t = MyThreadWithArgs(args=(i,), kwargs={‘a‘: ‘A‘, ‘b‘: ‘B‘})
    t.start()

MyThreadwithArgs使用的API与Thread相同,不过类似于其他定制类,这个类可以轻松地修改构造函数方法,以取得更多参数或者与线程用途更直接相关的不同参数。

1.6 定时器线程

有时出于某种原因需要派生Thread,Timer就是这样一个例子,Timer也包含在threading中。Timer在一个延迟之后开始工作,而且可以在这个延迟期间内的任意时刻被取消。

import threading
import time
import logging

def delayed():
    logging.debug(‘worker running‘)

logging.basicConfig(
    level=logging.DEBUG,
    format=‘(%(threadName)-10s) %(message)s‘,
)

t1 = threading.Timer(0.3, delayed)
t1.setName(‘t1‘)
t2 = threading.Timer(0.3, delayed)
t2.setName(‘t2‘)

logging.debug(‘starting timers‘)
t1.start()
t2.start()

logging.debug(‘waiting before canceling %s‘, t2.getName())
time.sleep(0.2)
logging.debug(‘canceling %s‘, t2.getName())
t2.cancel()
logging.debug(‘done‘)

这个例子中,第二个定时器永远不会运行,看起来第一个定时器在主程序的其余部分完成之后还会运行。由于这不是一个守护线程,所以在主线程完成时其会隐式退出。

1.7 线程间传送信号

尽管使用多线程的目的是并发地运行单独的操作,但有时也需要在两个或多个线程中同步操作。事件对象是实现线程间安全通信的一种简单方法。Event管理一个内部标志,调用者可以用set()和clear()方法控制这个标志。其他线程可以使用wait()暂停,直到这个标志被设置,可有效地阻塞进程直至允许这些线程继续。

import logging
import threading
import time

def wait_for_event(e):
    """Wait for the event to be set before doing anything"""
    logging.debug(‘wait_for_event starting‘)
    event_is_set = e.wait()
    logging.debug(‘event set: %s‘, event_is_set)

def wait_for_event_timeout(e, t):
    """Wait t seconds and then timeout"""
    while not e.is_set():
        logging.debug(‘wait_for_event_timeout starting‘)
        event_is_set = e.wait(t)
        logging.debug(‘event set: %s‘, event_is_set)
        if event_is_set:
            logging.debug(‘processing event‘)
        else:
            logging.debug(‘doing other work‘)

logging.basicConfig(
    level=logging.DEBUG,
    format=‘(%(threadName)-10s) %(message)s‘,
)

e = threading.Event()
t1 = threading.Thread(
    name=‘block‘,
    target=wait_for_event,
    args=(e,),
)
t1.start()

t2 = threading.Thread(
    name=‘nonblock‘,
    target=wait_for_event_timeout,
    args=(e, 2),
)
t2.start()

logging.debug(‘Waiting before calling Event.set()‘)
time.sleep(0.3)
e.set()
logging.debug(‘Event is set‘)

wait()方法取一个参数,表示等待事件的时间(秒数),达到这个时间后就超时。它
会返回一个布尔值,指示事件是否已设置,使调用者知道wait()为什么返回。可以对事
件单独地使用is_set()方法而不必担心阻塞。

在这个例子中,wait_for_event_timeout()将检查事件状态而不会无限阻塞。wait_for_event()在wait()调用的位置阻塞,事件状态改变之前它不会返回。

1.8 控制资源访问

除了同步线程操作,还有一点很重要,要能够控制对共享资源的访问,从而避免破坏或丢失数据。Python的内置数据结构(列表、字典等)是线程安全的,这是Python使用原子字节码来管理这些数据结构的一个副作用(更新过程中不会释放保护Python内部数据结构的全局解释器锁GIL(Global Interpreter Lock))。Python中实现的其他数据结构或更简单的类型(如整数和浮点数)则没有这个保护。要保证同时安全地访问一个对象,可以使用一个Lock对象。

import logging
import random
import threading
import time

class Counter:

    def __init__(self, start=0):
        self.lock = threading.Lock()
        self.value = start

    def increment(self):
        logging.debug(‘Waiting for lock‘)
        self.lock.acquire()
        try:
            logging.debug(‘Acquired lock‘)
            self.value = self.value + 1
        finally:
            self.lock.release()

def worker(c):
    for i in range(2):
        pause = random.random()
        logging.debug(‘Sleeping %0.02f‘, pause)
        time.sleep(pause)
        c.increment()
    logging.debug(‘Done‘)

logging.basicConfig(
    level=logging.DEBUG,
    format=‘(%(threadName)-10s) %(message)s‘,
)

counter = Counter()
for i in range(2):
    t = threading.Thread(target=worker, args=(counter,))
    t.start()

logging.debug(‘Waiting for worker threads‘)
main_thread = threading.main_thread()
for t in threading.enumerate():
    if t is not main_thread:
        t.join()
logging.debug(‘Counter: %d‘, counter.value)

在这个例子中,worker()函数使一个Counter实例递增,这个实例管理着一个Lock,以避免两个线程同时改变其内部状态。如果没有使用Lock,就有可能丢失一次对value属性的修改。

要确定是否有另一个线程请求这个锁而不影响当前线程,可以向acquire()的blocking参数传入False。在下一个例子中,worker()想要分别得到3次锁,并统计为得到锁而尝试的次数。与此同时,lock_holder()在占有和释放锁之间循环,每个状态会短暂暂停,以模拟负载情况。

import logging
import threading
import time

def lock_holder(lock):
    logging.debug(‘Starting‘)
    while True:
        lock.acquire()
        try:
            logging.debug(‘Holding‘)
            time.sleep(0.5)
        finally:
            logging.debug(‘Not holding‘)
            lock.release()
        time.sleep(0.5)

def worker(lock):
    logging.debug(‘Starting‘)
    num_tries = 0
    num_acquires = 0
    while num_acquires < 3:
        time.sleep(0.5)
        logging.debug(‘Trying to acquire‘)
        have_it = lock.acquire(0)
        try:
            num_tries += 1
            if have_it:
                logging.debug(‘Iteration %d: Acquired‘,
                              num_tries)
                num_acquires += 1
            else:
                logging.debug(‘Iteration %d: Not acquired‘,
                              num_tries)
        finally:
            if have_it:
                lock.release()
    logging.debug(‘Done after %d iterations‘, num_tries)

logging.basicConfig(
    level=logging.DEBUG,
    format=‘(%(threadName)-10s) %(message)s‘,
)

lock = threading.Lock()

holder = threading.Thread(
    target=lock_holder,
    args=(lock,),
    name=‘LockHolder‘,
    daemon=True,
)
holder.start()

worker = threading.Thread(
    target=worker,
    args=(lock,),
    name=‘Worker‘,
)
worker.start()

worker()需要超过3次迭代才能得到3次锁。

1.8.1 再入锁

正常的Lock对象不能请求多次,即使是由同一个线程请求也不例外。如果同一个调用链中的多个函数访问一个锁,则可能会产生我们不希望的副作用。

import threading

lock = threading.Lock()

print(‘First try :‘, lock.acquire())
print(‘Second try:‘, lock.acquire(0))

在这里,对第二个acquire()调用给定超时值为0,以避免阻塞,因为锁已经被第一个调用获得。

如果同一个线程的不同代码需要"重新获得"锁,那么在这种情况下要使用RLock。

import threading

lock = threading.RLock()

print(‘First try :‘, lock.acquire())
print(‘Second try:‘, lock.acquire(0))

与前面的例子相比,对代码唯一的修改就是用RLock替换Lock。

1.8.2 锁作为上下文管理器

锁实现了上下文管理器API,并与with语句兼容。使用with则不再需要显式地获得和释放锁。

import threading
import logging

def worker_with(lock):
    with lock:
        logging.debug(‘Lock acquired via with‘)

def worker_no_with(lock):
    lock.acquire()
    try:
        logging.debug(‘Lock acquired directly‘)
    finally:
        lock.release()

logging.basicConfig(
    level=logging.DEBUG,
    format=‘(%(threadName)-10s) %(message)s‘,
)

lock = threading.Lock()
w = threading.Thread(target=worker_with, args=(lock,))
nw = threading.Thread(target=worker_no_with, args=(lock,))

w.start()
nw.start()

函数worker_with()和worker_no_with()用等价的方式管理锁。

1.9 同步线程

除了使用Event,还可以通过使用一个Condition对象来同步线程。由于Condition使用了一个Lock,所以它可以绑定到一个共享资源,允许多个线程等待资源更新。在下一个例子中,consumer()线程要等待设置了Condition才能继续。producer()线程负责设置条件,以及通知其他线程继续。

import logging
import threading
import time

def consumer(cond):
    """wait for the condition and use the resource"""
    logging.debug(‘Starting consumer thread‘)
    with cond:
        cond.wait()
        logging.debug(‘Resource is available to consumer‘)

def producer(cond):
    """set up the resource to be used by the consumer"""
    logging.debug(‘Starting producer thread‘)
    with cond:
        logging.debug(‘Making resource available‘)
        cond.notifyAll()

logging.basicConfig(
    level=logging.DEBUG,
    format=‘%(asctime)s (%(threadName)-2s) %(message)s‘,
)

condition = threading.Condition()
c1 = threading.Thread(name=‘c1‘, target=consumer,
                      args=(condition,))
c2 = threading.Thread(name=‘c2‘, target=consumer,
                      args=(condition,))
p = threading.Thread(name=‘p‘, target=producer,
                     args=(condition,))

c1.start()
time.sleep(0.2)
c2.start()
time.sleep(0.2)
p.start()

这些线程使用with来获得与Condition关联的锁。也可以显式地使用acquire()和release()方法。

屏障(barrier)是另一种线程同步机制。Barrier会建立一个控制点,所有参与线程会在这里阻塞,直到所有这些参与“方”都到达这一点。采用这种方法,线程可以单独启动然后暂停,直到所有线程都准备好才可以继续。

import threading
import time

def worker(barrier):
    print(threading.current_thread().name,
          ‘waiting for barrier with {} others‘.format(
              barrier.n_waiting))
    worker_id = barrier.wait()
    print(threading.current_thread().name, ‘after barrier‘,
          worker_id)

NUM_THREADS = 3

barrier = threading.Barrier(NUM_THREADS)

threads = [
    threading.Thread(
        name=‘worker-%s‘ % i,
        target=worker,
        args=(barrier,),
    )
    for i in range(NUM_THREADS)
]

for t in threads:
    print(t.name, ‘starting‘)
    t.start()
    time.sleep(0.1)

for t in threads:
    t.join()

在这个例子中,Barrier被配置为会阻塞线程,直到3个线程都在等待。满足这个条件时,所有线程被同时释放从而越过这个控制点。wait()的返回值指示了释放的参与线程数,可以用来限制一些线程做清理资源等动作。

Barrier的abort()方法会使所有等待线程接收一个BrokenBarrierError。如果线程在wait()上被阻塞而停止处理,这就允许线程完成清理工作。

import threading
import time

def worker(barrier):
    print(threading.current_thread().name,
          ‘waiting for barrier with {} others‘.format(
              barrier.n_waiting))
    try:
        worker_id = barrier.wait()
    except threading.BrokenBarrierError:
        print(threading.current_thread().name, ‘aborting‘)
    else:
        print(threading.current_thread().name, ‘after barrier‘,
              worker_id)

NUM_THREADS = 3

barrier = threading.Barrier(NUM_THREADS + 1)

threads = [
    threading.Thread(
        name=‘worker-%s‘ % i,
        target=worker,
        args=(barrier,),
    )
    for i in range(NUM_THREADS)
]

for t in threads:
    print(t.name, ‘starting‘)
    t.start()
    time.sleep(0.1)

barrier.abort()

for t in threads:
    t.join()

这个例子将Barrier配置为多加一个线程,即需要比实际启动的线程再多一个参与线程,所以所有线程中的处理都会阻塞。在被阻塞的各个线程中,abort()调用会产生一个异常。

1.10 限制资源的并发访问

有时可能需要允许多个工作线程同时访问一个资源,但要限制总数。例如,连接池支持同时连接,但数目可能是固定的,或者一个网络应用可能支持固定数目的并发下载。这些连接就可以使用Semaphore来管理。

import logging
import threading
import time

class ActivePool:

    def __init__(self):
        super(ActivePool, self).__init__()
        self.active = []
        self.lock = threading.Lock()

    def makeActive(self, name):
        with self.lock:
            self.active.append(name)
            logging.debug(‘Running: %s‘, self.active)

    def makeInactive(self, name):
        with self.lock:
            self.active.remove(name)
            logging.debug(‘Running: %s‘, self.active)

def worker(s, pool):
    logging.debug(‘Waiting to join the pool‘)
    with s:
        name = threading.current_thread().getName()
        pool.makeActive(name)
        time.sleep(0.1)
        pool.makeInactive(name)

logging.basicConfig(
    level=logging.DEBUG,
    format=‘%(asctime)s (%(threadName)-2s) %(message)s‘,
)

pool = ActivePool()
s = threading.Semaphore(2)
for i in range(4):
    t = threading.Thread(
        target=worker,
        name=str(i),
        args=(s, pool),
    )
    t.start()

在这个例子中,ActivePool类只作为一种便利方法,用来跟踪某个给定时刻哪些线程能够运行。真正的资源池会为新的活动线程分配一个连接或另外某个值,并且当这个线程工作完成时再回收这个值。在这里,资源池只是用来保存活动线程的名,以显示至少有两个线程在并发运行。

1.11 线程特定的数据

有些资源需要锁定以便多个线程使用,另外一些资源则需要保护,以使它们对并非是这些资源的“所有者”的线程隐藏。local()函数会创建一个对象,它能够隐藏值,使其在不同线程中无法被看到。

import random
import threading
import logging

def show_value(data):
    try:
        val = data.value
    except AttributeError:
        logging.debug(‘No value yet‘)
    else:
        logging.debug(‘value=%s‘, val)

def worker(data):
    show_value(data)
    data.value = random.randint(1, 100)
    show_value(data)

logging.basicConfig(
    level=logging.DEBUG,
    format=‘(%(threadName)-10s) %(message)s‘,
)

local_data = threading.local()
show_value(local_data)
local_data.value = 1000
show_value(local_data)

for i in range(2):
    t = threading.Thread(target=worker, args=(local_data,))
    t.start()

属性local_data.value对所有线程都不可见,除非在某个线程中设置了这个属性,这个线程才能看到它。

要初始化设置以使所有线程在开始时都有相同的值,可以使用一个子类,并在_init_()中设置这些属性。

import random
import threading
import logging

def show_value(data):
    try:
        val = data.value
    except AttributeError:
        logging.debug(‘No value yet‘)
    else:
        logging.debug(‘value=%s‘, val)

def worker(data):
    show_value(data)
    data.value = random.randint(1, 100)
    show_value(data)

class MyLocal(threading.local):

    def __init__(self, value):
        super().__init__()
        logging.debug(‘Initializing %r‘, self)
        self.value = value

logging.basicConfig(
    level=logging.DEBUG,
    format=‘(%(threadName)-10s) %(message)s‘,
)

local_data = MyLocal(1000)
show_value(local_data)

for i in range(2):
    t = threading.Thread(target=worker, args=(local_data,))
    t.start()

这会在相同的对象上调用_init_()(注意id()值),每个线程中调用一次以设置默认值。

原文地址:https://www.cnblogs.com/liuhui0308/p/12592274.html

时间: 2024-08-13 11:28:30

Python3标准库:threading进程中管理并发操作的相关文章

Python3标准库:concurrent.futures管理并发任务池

1. concurrent.futures管理并发任务池 concurrent.futures模块提供了使用工作线程或进程池运行任务的接口.线程和进程池的API是一样的,所以应用只做最小的修改就可以在线程和进程之间顺利地切换. 这个模块提供了两种类型的类与这些池交互.执行器(executor)用来管理工作线程或进程池,future用来管理工作线程或进程计算的结果.要使用一个工作线程或进程池,应用要创建适当的执行器类的一个实例,然后向它提交任务来运行.每个任务启动时,会返回一个Future实例.需

Obstack是C标准库里面对内存管理的GNU扩展

Obstack介绍 Obstack初始化 在Obstack中申请对象 释放对象 申请growing object 获取Obstack状态 数据对齐 以下是来自wiki对obstack的介绍: Obstack是C标准库里面对内存管理的GNU扩展(实际上就是GNU C library了).Obstack===Object stack.没错,Obstack就是一个栈,栈里面的元素是对象object(不是面向对象的对象哦,这里的对象单指数据元素).这些数据是动态的,也就是使用的是动态内存.这种内存管理技

C++标准库vector类型的使用和操作总结

vector是一种类型对象的集合,它是一种顺序容器,容器中的所有对象必须都是同一种类型.vector的对象是可以动态生长的,这说明它在初始化时可以不用指定大小,而是再使用时根据元素所需的空间动态增长.C++中还有一种常见的类型string,它和vector有很多相似之处,具体可以看<C++标准库string类型的使用和操作总结>这篇博文.下面简单介绍一下vector容器的使用和操作. 一.声明和初始化 使用vector之前必须在程序前的库包含中包含相应的头文件,如下: #include<

数据库中的并发操作带来的一系列问题及解决方法

数据库中常见的并发操作所带来的一致性问题包括:丢失的修改.不可重复读.读脏数据.幻影读(幻影读在一些资料中往往与不可重复读归为一类). 丢失修改 下面我们先来看一个例子,说明并发操作带来的数据的不一致性问题. 考虑飞机订票系统中的一个活动序列: 甲售票点(甲事务)读出某航班的机票余额A,设A=16. 乙售票点(乙事务)读出同一航班的机票余额A,也为16. 甲售票点卖出一张机票,修改余额A←A-1.所以A为15,把A写回数据库. 乙售票点也卖出一张机票,修改余额A←A-1.所以A为15,把A写回数

Python3标准库

文本 1. string:通用字符串操作 2. re:正则表达式操作 3. difflib:差异计算工具 4. textwrap:文本填充 5. unicodedata:Unicode字符数据库 6. stringprep:互联网字符串准备工具 7. readline:GNU按行读取接口 8. rlcompleter:GNU按行读取的实现函数 二进制数据 9. struct:将字节解析为打包的二进制数据 10. codecs:注册表与基类的编解码器 数据类型 11. datetime:基于日期与

python语言线程标准库threading.local源码解读

本段源码可以学习的地方: 1. 考虑到效率问题,可以通过上下文的机制,在属性被访问的时候临时构建: 2. 可以重写一些魔术方法,比如 __new__ 方法,在调用 object.__new__(cls) 前后进行属性的一些小设置: 3. 在本库中使用的重写魔术方法,上下文这两种基础之上,我们可以想到函数装饰器,类装饰器,异常捕获,以及两种上下文的结构: 灵活运用这些手法,可以让我们在代码架构上更上一层,能够更加省时省力. 1 from weakref import ref # ref用在了构造大

python模块介绍-threading: 线程 管理并发操作

定义线程 最简单的方法:使用target指定线程要执行的目标函数,再使用start()启动. 语法: class threading.Thread(group=None, target=None, name=None, args=(), kwargs={}) group恒为None,保留未来使用.target为要执行的函数名.name为线程名,默认为Thread-N,通常使用默认即可.但服务器端程序线程功能不同时,建议命名. #!/usr/bin/env python3 # coding=utf

4.Python3标准库--算法

(一)functools:管理函数的工具 import functools ''' functools模块提供了一些工具来管理或扩展和其他callable对象,从而不必完全重写 ''' 1.修饰符 from functools import partial ''' functools模块提供的主要工具就是partial类,可以用来包装一个有默认参数的callable对象. 得到的对象本身就是callable,可以把它看作是原来的参数. ''' # 举个栗子 def foo(name, age,

Python3标准库:weakref对象的非永久引用

1. weakref对象的非永久引用 weakref模块支持对象的弱引用.正常的引用会增加对象的引用数,并避免它被垃圾回收.但结果并不总是如期望中的那样,比如有时可能会出现一个循环引用,或者有时需要内存时可能要删除对象的缓存.弱引用(weak reference)是一个不能避免对象被自动清理的对象句柄. 1.1 引用 对象的弱引用要通过ref类来管理.要获取原对象,可以调用引用对象. import weakref class ExpensiveObject: def __del__(self):