代码改变世界

进程、线程、协程(线程篇)

2018-12-23 17:51  ZpStu  阅读(173)  评论(0编辑  收藏  举报

    (对于线程篇的一些基本了解)

什么是线程?

  在一个进程的内部要同时干多件事,就需要同时运行多个子任务,我们把进程内的这些子任务叫做线程;

线程通常叫做轻型的进程,线程事共享内存空间的并发执行的多任务,每一个线程都共享一个进程的资源;

线程是最小的执行单元,而进程有至少一个线程组成;

如何调度进程和线程,完全由操作系统决定,程序自己是不能决定什么时候执行和执行多长时间。(这些有操作系统决定的)

  关于模块:

    1. _thread模块 低级模块(接近底层C语言写的)

    2. threading模块 高级模块,对_thread进行了封装

# 启动一个线程
import threading, time
def run(a):
    print('子线程--{}--开始'.format(threading.current_thread().name))
    # 实现线程的功能
    time.sleep(2)
    print('打印', a)
    print('子进程--{}--结束'.format(threading.current_thread().name))
if __name__ == '__main__':
    # 任何进程默认就会启动一个线程,成为主线程,主线程可以启动新的子线程
    print('主线程({})启动'.format(threading.current_thread()))
    # 创建子线程
    t = threading.Thread(target=run, args=(1,), name='runThread')
    t.start()
    # 等待线程结束
    t.join()
    print('主线程({})结束'.format(threading.currnet_thread()))
# 线程间共享数据
'''
    多线程和多进程最大的不同在于:多进程中,同一个变量,各自有一份拷贝在每个进程中,互不影响。
    而多线程中,所有变量都由所有线程共享。所以任何一个变量都可以被任意一个线程修改。
    因此线程之间共享数据最大的危险,多个线程同时修改一个变量,容易把内容改乱
'''
import threading
num = 100
def run(n):
    global num
    for i in range(100000):
        num += n
        num -= n
if __name__ == '__main__':
    t1 = threading.Thread(target=run, args=(6,))
    t2 = threading.Thread(target=run, args=(9,))
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print('num = ', num) 
# 你会发现修改的数字不一定是100,这个不知道要怎么解释,可能对一个数字调用次数太多,然后计数器混乱,共享资源混乱。
# 线程锁解决数据混乱
'''
    两个线程同时工作、一个存钱、一个取钱
    可能导致数据异常,就像上一个
    解决思路:
'''
import threading
lock = threading.Lock()
num = 100
def tun(n):
    global num
    for i inrange(100000):
        # 为线程加一把锁,确保这段代码只能有一个线程从头到尾完整执行,阻止多线程的并发执行,包含锁的某段代码实际上只能以单线程模式执行,所以效率大大降低了
        #优于它可以存在多个锁,不同线程持有不同的锁,并试图获取其他的锁,这样可能造成多个线程挂起,这样只能靠操作系统强制终止
'''
try:
    lock.acquire()
    num += n
    num -= n
finally:
    # 修改完一定要释放锁
    lock.release()
'''
# 与上面代码功能相同,with.lock可以自动上锁与解锁
with lock:
    num += n
    num -= n
if __name__ == '__main__':
  t1 = threading.Thread(target=run, args=(6,))
  t2 = threading.Thread(target=run, args=(9,))
  t1.start()
  t2.start()
  t1.join()
  t2.join()
  print('num = ', num)
# 这段代码就是执行略慢

 

import threading
num = 100
'''
创建一个全局的ThreadLocal对象
每个线程都有独立的存储空间,也就是线程栈
每个ThreadLocal对象都可以读写,而且互不影响
'''
local = threading.local()
def run(num, n):
    num += n
    num -= n
def func(n):
    global num
    # 每个线程都有local.x,就是线程的局部变量
    local.x = num
    for i in range(1000000):
        run(local.x, n)
    print('{}--{}'.format(threading.current_thread().name, local.x))
if __name__ == '__main__':
    t1 = threading.Thread(target=func(6,))
    t2 = threading.Thread(target=func(9,))
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print('num = ', num)
# 作用:因为每个线程栈是独立的,所以这就可以为每个线程绑定一个数据库链接,HTTP请求,用户身份信息等。
# 这样一个线程的所有调用到的处理函数都可以非常方便的访问这些资源
# 信号量控制线程数量
import threading, time
# 控制线程数量,也就是控制并发执行的线程执行数量
sem = threading.Semaphore(3)
def run():
    with sem:
        for i in range(10):
            print('{}--{}'.format(threading.current_thread().name, i))
if __name__ == '__main__':
    for i in range(5):
        threading.Thread(target=run).start()
# 筹够一定数量才能一起执行
import threading, time
'''
    threading.Barrier(parties, action=None, timeout=None)设置n=parties个线程,当n个barrier.wait()被调用后,所有这些调用的阻塞被同时解除,执行action
    barrier 创建一个障碍,必须达到指定数量的线程后才可以继续
    wait()当前进程进入阻塞状态
    about()强行突破阻碍,所有正在等待的线程和要调用wait()方法的线程收到一个BrokenBarrierError异常
    reset()重置当前对象,所有正在等待的线程收到一个BrokenBarrierErrir异常
''
bar= threading.Barrier(3)
def run():
    print('{}--start'.format(threading.current_thread().name))
    time.sleep(1)
    bar.wait()
    print('{}--end'.format(threading.current_thread().name))
if __name__ == '__main__':
    for i in range(5):
        threading.Thread(target=run).start()
# 定时线程
import threading
def tun():
    pass
# 延迟执行线程
t = threading.Timer(5, run)
t.start()
t.join()
print('父线程结束')
# 线程通信
import threading, time
def func():
    # 事件对象
    event = threading.Event()
    def run():
        for i in range(5):
            # 阻塞,等待时间的触发
            event.wait()
            # 这个是重置,没有clear(),就一起打印
            event.clear()
            print('zpstu is good man-{}'.format(i))

e = func()

# 触发事件
for i in range(5):
    time.sleep(2)
    e.set()
# 生产者与消费者
'''
就是一个队列,生产者生产东西放入这个队列,然后消费者消费东西拿出队列的那个东西。
'''
import threading, queue, time, random
# 生产者
def product(id, q):
    while True:
        num = random.randint(0, 10000)
        q.put(num)
        print('生产者{}生产了{}数据放入队列'.format(id, num))
        time.sleep(3)
    # 任务完成
    q.task_done()
# 消费者
def customer(id, q):
    while True:
        item = q.get()
        if item is None:
            break
        print('消费者{}消费了{}数据'.format(id, item))
        time.sleep(2)
if __name__ == '__main__':
    # 消息队列
    q = queue.Queue()
    # 启动生产者
    for i in range(4):
        threading.Thread(target=product, args=(i, q)).start()
    # 启动消费者
    for i in range(3):
        threading.Thread(target=customer, args=(i, q)).start()
# 线程调度
'''
这个就是A类通知B类可以动了,然后B动A阻塞;然后B类动完后通知A类你可以动了,然后B类阻塞,循环往复
'''
import threading, time
def run1():
    with cond:
        for i in range(0, 10, 2):
            print(threading.currnet_thread().name, i)
            time.sleep(1)
            cond.wait()
            cond.notify() # 英文意思是通知、通告
def run2():
    with cond:
        for i in range(1, 10, 2):
            print(threading.current_thread().name, i)
            time.sleep(1)
            cond.notify()
            cond.wait()
# 线程条件变量
cond = threading.Condition()
threading.Thread(target=run1).start()
threading.Thread(target=run2).start()