Python多线程及死锁预防

线程(英语: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)

你可能感兴趣的:(Python多线程及死锁预防)