线程(英语:thread)是操作系统能够进行运算调度的最小单位。
并发:指的是任务数比cpu核数多的时候,通过内核调度,实现用多个任务“一起”执行。
并行:指的是任务数小于等于cpu核数,即任务真的是一起执行的。
同步: 就是协同步调,按预定的先后次序进行运行。
互斥锁: 解决线程之间资源竞争不安全问题。
死锁: 多个线程获取多个锁,造成A需要B持有的锁,B需要A持有的锁。
防止死锁: 程序设计时要尽量避免, 添加超时时间,为程序中的每一个锁分配一个唯一的id。
同步源语:
当一个进程调用一个send原语时,在消息开始发送后,发送进程便处于阻塞状态,直至消息完全发送完毕,send原语的后继语句才能继续执行。当一个进程调用一个receive原语时,并不立即返回控制,而是等到把消息实际接收下来,并把它放入指定的接收区,才返回控制,继续执行该原语的后继指令。在这段时间它一直处于阻塞状态。上述的send和receive被称为同步通信原语或阻塞通信原语。
python多线程之间共享数据
- 全局变量
- 传递参数
- 使用queue
创建基本的线程
# Code to execute in an independent thread
import time
def countdown(n):
while n > 0:
print('T-minus', n)
n -= 1
time.sleep(5)
# Create and launch a thread
from threading import Thread
t = Thread(target=countdown, args=(10,))
t.start()
默认,主线程会等待子线程执行完之后执行。
Queue 线程之间通信
from queue import Queue
from threading import Thread
# A thread that produces data
def producer(out_q):
while True:
# Produce some data
...
out_q.put(data)
# A thread that consumes data
def consumer(in_q):
while True:
# Get some data
data = in_q.get()
# Process the data
...
# Create the shared queue and launch both threads
q = Queue()
t1 = Thread(target=consumer, args=(q,))
t2 = Thread(target=producer, args=(q,))
t1.start()
t2.start()
Queue 对象已经包含了必要的锁。
给原子性操作加锁
import threading
class SharedCounter:
'''
A counter object that can be shared by multiple threads.
'''
def __init__(self, initial_value = 0):
self._value = initial_value
self._value_lock = threading.Lock()
def incr(self,delta=1):
'''
Increment the counter with locking
'''
with self._value_lock:
self._value += delta
def decr(self,delta=1):
'''
Decrement the counter with locking
'''
with self._value_lock:
self._value -= delta
使用threading.Lock()
可以得到一个互斥锁。
# 创建锁
mutex = threading.Lock()
# 锁定
mutex.acquire()
# 释放
mutex.release()
一个线程获取多个锁如何避免死锁
将获取锁的顺序规定,如,id(lock)从小到大排序。
import threading
from contextlib import contextmanager
import time
# Thread-local state to stored information on locks already acquired
_local = threading.local()
@contextmanager
def acquire(flag, *locks):
print(flag, 'start......................')
# Sort locks by object identifier
locks = sorted(locks, key=lambda x: id(x))
# Make sure lock order of previously acquired locks is not violated
acquired = getattr(_local, 'acquired', [])
if acquired and max(id(lock) for lock in acquired) >= id(locks[0]):
raise RuntimeError('Lock Order Violation')
# Acquire all of the locks
acquired.extend(locks)
print(flag, 'acquired:', acquired)
_local.acquired = acquired
try:
for lock in locks:
print(flag, 'start lock acquired for:', lock)
lock.acquire()
print(flag, 'end lock acquired for:', lock)
print(flag, 'end......................')
yield
finally:
# Release locks in reverse order of acquisition
for lock in reversed(locks):
lock.release()
del acquired[-len(locks):]
import threading
x_lock = threading.Lock()
y_lock = threading.Lock()
g_num = 0
def thread_1():
cur_tim = time.time()
FLAG = True
global g_num
while FLAG:
if time.time() - cur_tim > 1:
FLAG = False
with acquire('thread1', y_lock, x_lock):
print('Thread-1')
g_num += 1
def thread_2():
cur_tim = time.time()
FLAG = True
global g_num
while FLAG:
if time.time() - cur_tim > 1:
FLAG = False
with acquire('thread2',y_lock, x_lock):
print('Thread-2')
g_num += 1
t1 = threading.Thread(target=thread_1)
t1.start()
t2 = threading.Thread(target=thread_2)
t2.start()
time.sleep(2)