多线程

线程和进程的区别:

进程之间切换非常消耗资源

线程之间切换相对来说节省资源

 

使用线程的场景:

多进程和多线程的数据共享的区别

# 多进程  最后执行打印结果两个100
from multiprocessing import Process
def func1():
    global g
    g = 0

if __name__ == '__main__':
    g = 100
    print(g)
    p = Process(target=func1)
    p.start()
    p.join()
    print(g)



# 多线程  最后执行打印结果两个第一个100,第二个0
from threading import Thread def func1(): global g g = 0 if __name__ == '__main__': g = 100 print(g) t = Thread(target=func1) t.start() t.join() print(g)

  

全局解释锁GIL锁住的不是线程共享的数据,而是所有的进程。

虽然有了全局解释锁GIL,但是还不是最安全的,因为比如以下情况:

这种情况下我想着线程有了全局解释锁,最后结果应该是0,打印出来结果却是9,因为中间我们人为让程序睡眠了一秒(真实情况下可能由于时间片轮转到了下一个线程),

所以这个时候就需要加锁了。

科学家吃面引出来的死锁问题:

from threading import Thread, Lock
import time
noodle_lock = Lock()
fork_look = Lock()
def eat1(name):
    noodle_lock.acquire()
    print("{}拿到面了".format(name))
    fork_look.acquire()
    print("{}拿到叉子了".format(name))
    print("{}开始吃面了".format(name))
    noodle_lock.release()
    fork_look.release()


def eat2(name):
    fork_look.acquire()
    print("{}拿到叉子了".format(name))
    time.sleep(1)
    noodle_lock.acquire()
    print("{}拿到面了".format(name))
    print("{}开始吃面了".format(name))
    fork_look.release()
    noodle_lock.release()

if __name__ == '__main__':
    Thread(target=eat1, args=("alex", )).start()
    Thread(target=eat2, args=("egon", )).start()
    Thread(target=eat1, args=("nezha", )).start()
    Thread(target=eat2, args=("bossjin", )).start()

  

科学家吃面引出来的递归锁问题:

from threading import Thread, RLock
import time
fork_look = noodle_lock = RLock()
def eat1(name):
    noodle_lock.acquire()
    print("{}拿到面了".format(name))
    fork_look.acquire()
    print("{}拿到叉子了".format(name))
    print("{}开始吃面了".format(name))
    noodle_lock.release()
    fork_look.release()


def eat2(name):
    fork_look.acquire()
    print("{}拿到叉子了".format(name))
    time.sleep(1)
    noodle_lock.acquire()
    print("{}拿到面了".format(name))
    print("{}开始吃面了".format(name))
    fork_look.release()
    noodle_lock.release()

if __name__ == '__main__':
    Thread(target=eat1, args=("alex", )).start()
    Thread(target=eat2, args=("egon", )).start()
    Thread(target=eat1, args=("nezha", )).start()
    Thread(target=eat2, args=("bossjin", )).start()

  

 

同一个进程或线程中用到两把或两把以上锁的时候,就容易产生死锁现象,这个时候把互斥锁改为递归锁,就能规避这种情况。

RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。

进程中的数据本身就不共享,所以很少会用到加锁的情况,而线程则是数据共享的,所以需要加锁  

线程中的事件例子:

from threading import Thread, Event
import random
import time

e = Event()

def check_web():
    time.sleep(random.randint(3, 6))
    e.set()

def conn_db():
    count = 1
    while count < 4:
        e.wait(1)
        if e.is_set():
            print("连接数据库成功")
            break
        else:
            print("第{}次连接数据库失败".format(count))
            count += 1
    else:
        raise TimeoutError("连接数据库超时")



if __name__ == '__main__':
    Thread(target=check_web).start()
    Thread(target=conn_db).start()

  

线程中的定时器

from threading import Thread, Timer
def func():
    print("五秒后的线程开启")

if __name__ == '__main__':
    Timer(interval=5, function=func).start()

  

线程池中的队列:

# 多线程中的数据已经是共享的了,为什么还要用队列?
# 比如a线程要修改共享的一个列表中的一个元素,刚拿回来数据准备修改,时间片到了b线程,
# 这个时候b又去修改这个元素,就会造成数据的不安全,这个时候可以想到应该加锁,
# 而队列中就是加好锁了,供我们使用。


import queue
q = queue.Queue()  #队列,先进先出
q.put(1)
q.put(2)
print(q.get())


q = queue.LifoQueue()  #栈,先进后出
q.put(1)
q.put(2)
print(q.get())


q = queue.PriorityQueue()  #优先级队列
q.put((10, "a"))
q.put((1, "b"))
q.put((8, "c"))
print(q.get())

  

线程池

# 线程池里面的线程个数最多不要超过cpu个数乘以五
from concurrent.futures import ThreadPoolExecutor
import time


def func(n):
    print(n)
    time.sleep(1)
    return n * n

if __name__ == '__main__':
    pool = ThreadPoolExecutor(max_workers=5)
    for i in range(1, 21):
        p = pool.submit(func, i)
    pool.shutdown() #shutdown起到了close和join两个效果,close不让往线程池中扔任务,join等待线程池中的任务执行完毕
    print("主线程")

  

原文地址:https://www.cnblogs.com/QimiSun/p/10578002.html