首页 > 技术文章 > 初识进程 线程 协程(二):线程

xiaoqichaoren 2020-02-06 12:34 原文

线程:

1进程-->n线程
    多个线程共用进程资源

创建线程:

def func(n):
    ......
t = Thread(target=func, name = '', args = (1, ), kwargs = '')
t.start()

 

自定义线程:

class MyThread(Thread):
    def __init__(self, n):
        super(MyThread, self).__init__()
        self.n = n
    def run(self):
        ......
p = MyThread(args = (1,))
p.start()

重定义了run方法

数据共享:

    进程共享数据与线程共享数据的区别
        进程:每个进程中都有一份一样的数据
        线程:共用同一份数据,可能导致数据安全性问题

ticket = 0

def task1():
    global ticket
    for i in range(1000000):
        ticket += 1
    print('task1-->', ticket)

def task2():
    global ticket
    for i in range(1000000):
        ticket += 2
    print('task2-->', ticket)


if __name__ == '__main__':
    t1 = threading.Thread(target=task1)
    t2 = threading.Thread(target=task2)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(ticket)

'''
运行结果:
task1--> 2014411
task2--> 2025288
2025288
'''

共用了ticket,导致数据不安全,需要引入GIL锁

GIL锁:全局解释器锁

使线程变成伪线程:一个运行多个阻塞,交替运行。对于数据共享保证了安全性,但效率较低

lock = Lock()
lock.acquire()
......
lock.release()
lock = threading.Lock()
ticket = 0

def task1():
    global ticket
    lock.acquire()
    for i in range(1000000):
        ticket += 1
    print('task1-->', ticket)
    lock.release()

def task2():
    global ticket
    lock.acquire()
    for i in range(1000000):
        ticket += 2
    print('task2-->', ticket)
    lock.release()


if __name__ == '__main__':
    t1 = threading.Thread(target=task1)
    t2 = threading.Thread(target=task2)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(ticket)

'''
task1--> 1000000
task2--> 3000000
3000000
'''

加了锁之后,同样一个问题,可以保证数据安全,但是由于锁的存在,会使效率降低

出现多把锁,会出现死锁问题

死锁:

示例:

lockA = Lock()
lockB = Lock()

class MyThread1(Thread):
    def __init__(self, name):
        super(MyThread1, self).__init__()
        self.name = name

    def run(self):
        if lockA.acquire():     # 若获取到值,返回true
            print(self.name+'获取了A锁')
            time.sleep(0.2)     # 转让cpu执行权,让另外的线程执行
            if lockB.acquire():
                print(self.name+'获取了A锁和B锁')
                lockB.release()
            lockA.release()

class MyThread2(Thread):
    def __init__(self, name):
        super(MyThread2, self).__init__()
        self.name = name

    def run(self):
        if lockB.acquire():
            print(self.name+'获取了B锁')
            time.sleep(0.2)
            if lockA.acquire():
                print(self.name+'获取了A锁和B锁')
                lockA.release()
            lockB.release()


if __name__ == '__main__':
    t1 = MyThread1('线程1')
    t2 = MyThread2('线程2')

    t1.start()  # 占有A锁且等待B锁
    t2.start()  # 占有B锁且等待A锁

t1,t2 出现占有且等待的阻塞条件,发生死锁

避免死锁的方法:

1.重构代码

2.给申请锁请求加timeout,lock.acquire(timeout=0.5)。如果0.5秒后还没有申请到锁,则返回False

线程间的通信(利用队列):

生产者与消费者(利用队列)
def produce(q):   # 生产者
    while True:
        ......
        q.put(...)
        ......
    q.put(None)     # 队列尾端的标志
    q.task_done()   # 完成任务
def consume(q):
    while True:
        ......
        item = q.get()
        ......
        if item is None:    # 表明到达队尾,退出队列
            break
    q.task_done()   # 完成任务
if __name__ == '__main__':
    q = queue.Queue(10)
    tp = threading.Thread(target=produce, args=(q,))    # 创建生产者
    tc = threading.Thread(target=consume, args=(q,))    # 创建消费者
    tp.start()
    tc.start()
    tp.join()   # 先生产,后消费
    tc.join()

队列的特点是 先放,再取

推荐阅读