首页 > 编程语言 > 详细

线程 *知识点扩充

时间:2019-08-14 21:03:10      阅读:75      评论:0      收藏:0      [点我收藏+]

1.  基于 TCP 协议实现 socket 通信 多线程并发 效果(简易版本)


# 客户端
import socket

client = socket.socket()
client.connect((127.0.0.1, 8083))

while True:
    client.send(bhello)
    data = client.recv(1024)
    print(data.decode(utf-8))

# 服务端
import socket
from threading import Thread

def run(conn, i):
    while True:
        try:
            data = conn.recv(1024)
            print(第%s条数据: % i, data.decode(utf-8))
            conn.send(data.upper())
        except ConnectionResetError as e:
            print(e)
            break
    conn.close()

server = socket.socket()
server.bind((127.0.0.1, 8083))
server.listen(5)

while True:
    conn, addr = server.accept()
    for i in range(10):
        t = Thread(target=run, args=(conn, i))
        t.start()

 2. 全局解释器锁 GIL(global interpreter lock)

  GIL本质也是一把互斥锁:将并发变成串行牺牲效率,保证数据的安全

  作用:阻止同一个进程下的多个线程的同时执行

      也就意味着 同一个进程内多个线程无法实现并行但是可以实现并发

      python的多线程没法利用多核优势  是不是就是没有用了?   

          研究python的多线程是否有用需要分情况讨论:(肯定是有用的)

             假设有四个任务(每个任务都需要10s)

              都是计算密集型:

                单核情况下
                      开线程更省资源
                多核情况下
                      开进程 10+s
                      开线程 40+s

              都是 IO 密集型 :
                单核情况下
                      开线程更节省资源
                多核情况下

                   开线程更节省资源

 

  PS:python 解释器有很多种,最常见的就是 CPython 解释器

 

     GIL 的存在是因为 CPython 解释器的 内存管理的线程不是安全 的 

 

        如:多个线程同时处理同一进程下的一份数据,假如没有全局解释器锁,容易造成数据的错乱

 

# 计算密集型
# from multiprocessing import Process
# from threading import Thread
# import os,time
# def work():
#     res=0
#     for i in range(100000000):
#         res*=i
#
#
# if __name__ == ‘__main__‘:
#     l=[]
#     print(os.cpu_count())  # 本机为6核
#     start=time.time()
#     for i in range(6):
#         # p=Process(target=work) #耗时  4.732933044433594
#         p=Thread(target=work) #耗时 22.83087730407715
#         l.append(p)
#         p.start()
#     for p in l:
#         p.join()
#     stop=time.time()
#     print(‘run time is %s‘ %(stop-start))

 

# IO密集型
from multiprocessing import Process
from threading import Thread
import threading
import os,time
def work():
    time.sleep(2)


if __name__ == __main__:
    l=[]
    print(os.cpu_count()) #本机为6核
    start=time.time()
    for i in range(4000):
        p=Process(target=work) #耗时9.001083612442017s多,大部分时间耗费在创建进程上
        # p=Thread(target=work) #耗时2.051966667175293s多
        l.append(p)
        p.start()
    for p in l:
        p.join()
    stop=time.time()
    print(run time is %s %(stop-start))

 

3. 全局解释器 GIL & 普通互斥锁

我们从 全局解释器锁  未加锁但是让程序出现IO操作情况  以及  加锁 情况来比较
# 全局解释器锁

def run(i):
    global n
    re_n = n
    n = re_n - 1
    print(f第{i}个线程修改了n,现在 n = {n})


if __name__ == __main__:
    for i in range(10):
        t = Thread(target=run, args=(i,))
        t.start()


# 由于有全局解释器锁的存在,多线程操作数据正常

‘‘‘
# 第0个线程修改了n,现在n = 99
        # 第1个线程修改了n,现在n = 98
        # 第2个线程修改了n,现在n = 97
        # 第3个线程修改了n,现在n = 96
        # 第4个线程修改了n,现在n = 95
        # 第5个线程修改了n,现在n = 94
        # 第6个线程修改了n,现在n = 93
        # 第7个线程修改了n,现在n = 92
        # 第8个线程修改了n,现在n = 91
        # 第9个线程修改了n,现在n = 90
        # 
        # Process finished with exit code 0
‘‘‘

 

# 不加锁但是 让程序出现IO操作

from threading import Thread
import time

n = 100


def run(i):
    global n
    re_n = n
    time.sleep(1)  # IO操作时候回自动释放GIL
    n = re_n - 1
    print(f第{i}个线程修改了n,现在 n = {n})


if __name__ == __main__:
    for i in range(10):
        t = Thread(target=run, args=(i,))
        t.start()

# 由于多线程程序遇到了IO操作,使得数据错乱
‘‘‘
    # 第1个线程修改了n,现在 n = 99
        # 第0个线程修改了n,现在 n = 99
        # 第6个线程修改了n,现在 n = 99
        # 第4个线程修改了n,现在 n = 99
        # 第2个线程修改了n,现在 n = 99
        # 第3个线程修改了n,现在 n = 99
        # 第7个线程修改了n,现在 n = 99
        # 第5个线程修改了n,现在 n = 99
        # 第8个线程修改了n,现在 n = 99
        # 第9个线程修改了n,现在 n = 99
        # 
        # Process finished with exit code 0
‘‘‘

 

# 加锁
from threading import Thread, Lock
import time


mutex = Lock()  # 生成一把锁

n = 100


def run(i):
    mutex.acquire()  # 抢锁
    global n  # 局部修改全局
    re_n = n
    time.sleep(1)  # IO操作时候回自动释放GIL,但是由于自己加了一把锁,所以还是在修改数据时候并发变成串行
    n = re_n - 1
    print(f第{i}个线程修改了n,现在 n = {n})
    mutex.release()  # 释放锁


if __name__ == __main__:
    for i in range(10):
        t = Thread(target=run, args=(i,))
        t.start()

# 加锁情况下,数据得到了保障,结果正常  此时同时有 GIL 和 锁
‘‘‘
第0个线程修改了n,现在 n = 99
第1个线程修改了n,现在 n = 98
第2个线程修改了n,现在 n = 97
第3个线程修改了n,现在 n = 96
第4个线程修改了n,现在 n = 95
第5个线程修改了n,现在 n = 94
第6个线程修改了n,现在 n = 93
第7个线程修改了n,现在 n = 92
第8个线程修改了n,现在 n = 91
第9个线程修改了n,现在 n = 90

Process finished with exit code 0
‘‘‘

 

 4. 死锁现象(了解知识点)

from threading import Thread, Lock
import time

mutex1 = Lock()
mutex2 = Lock()

def run(i):
    run1(i)
    run2(i)


def run1(i):
    mutex1.acquire()
    print(f‘‘‘
    
第 {i} 个线程 第一次 抢到了 mutex1 锁‘‘‘)
    mutex2.acquire()
    print(f第 {i} 个线程 第一次 抢到了 mutex2 锁)
    mutex2.release()
    mutex1.release()


def run2(i):
    mutex2.acquire()
    print(f‘‘‘
    
第 {i} 个线程 第二次 抢到了 mutex2 锁‘‘‘)
    time.sleep(1)
    mutex1.acquire()
    print(f第 {i} 个线程 第二次 抢到了 mutex1 锁)
    mutex1.release()
    mutex2.release()


if __name__ == __main__:
    for i in range(5):
        t = Thread(target=run, args=(i,))
        t.start()

# 结果
‘‘‘
第 0 个线程 第一次 抢到了 mutex1 锁
第 0 个线程 第一次 抢到了 mutex2 锁

    
第 0 个线程 第二次 抢到了 mutex2 锁

    
第 1 个线程 第一次 抢到了 mutex1 锁
‘‘‘
# 上述结果可以看到,由于第0个线程在第二次抢锁时候,刚抢完锁1,准备抢锁2时候,发现锁2已经被线程1给抢走了,只能继续停在原地,代码执行不动,也就是死锁

# RLock(递归锁)
"""
Rlock可以被第一个抢到锁的人连续的acquire和release
每acquire一次锁身上的计数加1
每release一次锁身上的计数减1
只要锁的计数不为0 其他人都不能抢

"""
from threading import Thread, RLock
import time


mutex1 = mutex2 = RLock()  # Rlock可以被第一个抢到锁的人连续的acquire和release
# 此时 mutex1 和 mutex2 是同一把锁

def run(i):
    run1(i)
    run2(i)


def run1(i):
    mutex1.acquire()
    print(f‘‘‘
    
第 {i} 个线程 第一次 抢到了 mutex1 锁‘‘‘)
    mutex2.acquire()
    print(f第 {i} 个线程 第一次 抢到了 mutex2 锁)
    mutex2.release()
    mutex1.release()


def run2(i):
    mutex2.acquire()
    print(f‘‘‘
    
第 {i} 个线程 第二次 抢到了 mutex2 锁‘‘‘)
    time.sleep(1)
    mutex1.acquire()
    print(f第 {i} 个线程 第二次 抢到了 mutex1 锁)
    mutex1.release()
    mutex2.release()


if __name__ == __main__:
    for i in range(5):
        t = Thread(target=run, args=(i,))
        t.start()

# 结果
第 0 个线程 第一次 抢到了 mutex1 锁
第 0 个线程 第一次 抢到了 mutex2 锁

    
第 0 个线程 第二次 抢到了 mutex2 锁
第 0 个线程 第二次 抢到了 mutex1 锁

    
第 1 个线程 第一次 抢到了 mutex1 锁
第 1 个线程 第一次 抢到了 mutex2 锁

    
第 1 个线程 第二次 抢到了 mutex2 锁
第 1 个线程 第二次 抢到了 mutex1 锁

    
第 3 个线程 第一次 抢到了 mutex1 锁
第 3 个线程 第一次 抢到了 mutex2 锁

    
第 3 个线程 第二次 抢到了 mutex2 锁
第 3 个线程 第二次 抢到了 mutex1 锁

    
第 2 个线程 第一次 抢到了 mutex1 锁
第 2 个线程 第一次 抢到了 mutex2 锁

    
第 2 个线程 第二次 抢到了 mutex2 锁
第 2 个线程 第二次 抢到了 mutex1 锁

    
第 4 个线程 第一次 抢到了 mutex1 锁
第 4 个线程 第一次 抢到了 mutex2 锁

    
第 4 个线程 第二次 抢到了 mutex2 锁
第 4 个线程 第二次 抢到了 mutex1 锁

Process finished with exit code 0

 5. 信号量(了解知识点)

from threading import Semaphore, Thread
import time, random

sm = Semaphore(5)  # 5表示容量个数


def run(i):
    sm.acquire()  # 能同时接纳5个
    print(f{i}占了一个位置)
    time.sleep(random.randint(1, 3))
    sm.release()  # 释放一个空出来一个,可以继续接纳
    print(f{i}释放了一个位置)


if __name__ == __main__:
    for i in range(10):
        t = Thread(target=run, args=(i,))
        t.start()

# 结果
0占了一个位置
1占了一个位置
2占了一个位置
3占了一个位置
4占了一个位置
3释放了一个位置
    
5占了一个位置
1释放了一个位置
    
6占了一个位置
0释放了一个位置
7占了一个位置
2释放了一个位置
    
8占了一个位置

4释放了一个位置
    
6释放了一个位置
    
9占了一个位置
5释放了一个位置
    
9释放了一个位置
    
8释放了一个位置
    
7释放了一个位置
    

Process finished with exit code 0

 6. event 事件(了解知识点)

from threading import Event, Thread
import time

e = Event()  # 生成一个Event对象


def run():
    print(      红灯....)
    time.sleep(3)
    e.set()  # 发信号
    print(      绿灯... )


t = Thread(target=run)
t.start()


def run1(i):
    print(f公交 {i + 1} 路停车等待....)
    e.wait()  # 等待信号
    print(f公交 {i + 1} 路开始百公里加速....)


for i in range(10):
    t = Thread(target=run1, args=(i,))
    t.start()

# 结果
‘‘‘
      红灯....
公交 1 路停车等待....
公交 2 路停车等待....
公交 3 路停车等待....
公交 4 路停车等待....
公交 5 路停车等待....
公交 6 路停车等待....
公交 7 路停车等待....
公交 8 路停车等待....
公交 9 路停车等待....
公交 10 路停车等待....
      绿灯... 
公交 1 路开始百公里加速....
公交 4 路开始百公里加速....
公交 5 路开始百公里加速....
公交 8 路开始百公里加速....
公交 9 路开始百公里加速....
公交 2 路开始百公里加速....
公交 10 路开始百公里加速....
公交 6 路开始百公里加速....
公交 3 路开始百公里加速....
公交 7 路开始百公里加速....

Process finished with exit code 0
‘‘‘

7. 线程 q(了解知识点)

from queue import Queue, LifoQueue, PriorityQueue


q1 = Queue()  # 先进先出队列
q1.put(2)  # 先放
q1.put(1)
q1.put(3)

print(q1.get())  # 2

q2 = LifoQueue()  # 后进先出队列 last in first out
q2.put(2)
q2.put(3)
q2.put(5)  # 后放

print(q2.get())  # 5

q3 = PriorityQueue()  # 优先级队列,一个元祖参数,元祖内第一个参数表示优先级,越小越优先,支持负数;元组内第二个参数为要放入的数据
q3.put((10, 4))
q3.put((20, 5))
q3.put((-10, 6))  # 优先级最高
q3.put((0, 7))

print(q3.get())  # (-10, 6)
print(q3.get()[1])  # 6 元祖是有序的,支持索引取值

 

线程 *知识点扩充

原文:https://www.cnblogs.com/pupy/p/11354429.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!