day34

一丶线程的理论知识

什么是线程:

========================================================================

1.线程是一堆指令,是操作系统调度的最小单位

2.线程具有执行能力


3.线程依赖于进程

4.具有主从关系(人为定义,每一个进程都至少有一个主线程

二丶开启线程的两种方式(Thread)

类的方式开启线程

<pre class="md-fences md-end-block ty-contain-cm modeLoaded">### 利用到Thread<br></br>​<br></br>from  threading import Thread<br></br>​<br></br>class MyThread(Thread):<br></br>   <br></br>    def run(self) -> None:      # 必须重写run函数<br></br>        print(f'{self.name} 被开启了~~')<br></br>        <br></br>        <br></br>if __name__ == '__main__':<br></br>    t=MyThread()                # 实例化一个自定义线程类对象<br></br>    t.start()                   # 启动子线程<br></br>    print('in 主线程')

函数的方式开启线程

<pre class="md-fences md-end-block ty-contain-cm modeLoaded">### 函数开启线程<br></br>from  threading import Thread<br></br>​<br></br>def task(name):<br></br>    print(f'在子线程中: {name}')<br></br>        <br></br>​<br></br>if __name__ == '__main__':<br></br>    <br></br>    t=Thread(target=task,args=('abc',))        # 实例化线程对象<br></br>    t.start()                               # 启动子线程<br></br>    <br></br>    print('主~~~~')

三丶线程和进程之间的对比

进程VS线程:

1.线程的启动速度 快于 进程的启动速度

2.线程之间数据可以共享,进程之间不能数据共享(必须依靠队列才能实现)

3.线程开销小,进程开销大

4.在运行速度上,进程和线程是没有可比性.(两个不同的概念,线程具有执行能力,进程不具有执行能力)

四丶线程的其他方法

两种:

1.线程对象方法

2.threading对象方法

<pre class="md-fences md-end-block ty-contain-cm modeLoaded"># -*-coding:utf-8-*-<br></br># Author:Ds<br></br>​<br></br>from threading import Thread<br></br>import threading<br></br>import time<br></br>​<br></br>​<br></br>### 定义开启子线的方法<br></br>def task():<br></br>    time.sleep(1)       # 睡1秒, 保证所有的子线程都能存活1秒 以上<br></br>    print(f'123')<br></br>    print(threading.current_thread().name)  #子线程对象名字<br></br>​<br></br>​<br></br>    <br></br>if __name__ == '__main__':<br></br>    for i in range(6):<br></br>        t=Thread(target=task,name='firstIn')    #target指定子线程任务,name子线程名<br></br>        t.start()<br></br>​<br></br>        <br></br>        <br></br>    ### 1. 线程对象方法<br></br>    print(t.isAlive())  # 判断线程是否还存活<br></br>    t.setName('32141')  # 设置name属性<br></br>    print(t.getName()) # 获得线程名<br></br>​<br></br>    <br></br>​<br></br>    ### 2 threading模块方法<br></br>    print(threading.current_thread())  #线程对象<br></br>    print(threading.enumerate())        # 列表 [<_MainThread(MainThread, started 10748)>, <Thread(firstIn, started 3696)>,]<br></br>    print(threading.active_count())     # 获取活跃的线程数量  (包括主线程 )  7个<br></br>    print('主线程')

五丶守护进程

什么是守护线程:

1.守护线程必须等待所有的非守护线程以及主线程结束之后才结束

2.本质还是子线程,在开启前被设置成守护线程.

<pre class="md-fences md-end-block ty-contain-cm modeLoaded">### 守护线程<br></br>​<br></br>from threading import Thread<br></br>import time<br></br>​<br></br>def task(name):<br></br>    print(f'{name} is running')<br></br>    time.sleep(1)               # 当主线程结束了,当前守护线程也就结束了<br></br>    print(f'{name} is over')<br></br>​<br></br>​<br></br>​<br></br>if __name__ == '__main__':<br></br>    t = Thread(target=task,args=('abc',))<br></br>    t.daemon = True     # 将一个子线程设置为守护线程<br></br>    t.start()<br></br>​<br></br>    print('主线程')<br></br>​<br></br>​

容易产生歧义:

1.首先 要明确, 在同一时刻, CPU只允许一个任务的存在, 遇到IO阻塞进行任务切换

2.守护进程VS守护线程

守护线程:必须等待所有的非守护线程执行完毕以及主线程执行完毕,才能结束

守护进程:守护进程不会等待所有的非守护进程完毕才结束.只要主进程GG,守护进程GG

<pre class="md-fences md-end-block ty-contain-cm modeLoaded">### 守护线程:   <br></br>​<br></br>from threading import Thread<br></br>import time<br></br>​<br></br>def foo():<br></br>    print(123)<br></br>    time.sleep(1)       # sleep  表示要切换, 执行bar子线程<br></br>    print("end123")<br></br>​<br></br>def bar():<br></br>    print(456)          # 执行    守护线程必须等待非守护线程执行完毕才能结束<br></br>    time.sleep(3)<br></br>    print("end456")<br></br>​<br></br>if __name__ == '__main__':<br></br>​<br></br>    t1=Thread(target=foo)<br></br>    t2=Thread(target=bar)<br></br>​<br></br>    t1.daemon = True<br></br>    t1.start()<br></br>    t2.start()<br></br>    print("main-------")

六丶互斥锁

含义:

1.互斥锁,同步锁,锁.都是同一种锁LOCK

2.在并发时,保证数据的安全('串行')

<pre class="md-fences md-end-block ty-contain-cm modeLoaded">###互斥锁 实现 '并发' <br></br>​<br></br># -*-coding:utf-8-*-<br></br># Author:Ds<br></br>​<br></br>from threading import Thread<br></br>from threading import Lock      # 这是线程的 互斥锁<br></br>​<br></br>import  time<br></br>​<br></br>x=100<br></br>​<br></br>def task(lock):<br></br>    lock.acquire()      #  加锁 实现 '并发'  ,保证数据的安全性<br></br>    global x<br></br>    temp = x<br></br>    time.sleep(0.1)<br></br>    temp -= 1<br></br>    x = temp<br></br>    print(x)<br></br>    lock.release()<br></br>​<br></br>​<br></br>if __name__ == '__main__':<br></br>    lock=Lock()         # 实例化锁对象<br></br>​<br></br>####  如果想要实现并发.  1.把所有的实例对象都添加列表中,2 join循环列表, 每一个线程都必须等待上一个线程执行完,必须等待上一个线程执行完<br></br>​<br></br>    t_li=[]  # 存放线程对象, 实现并发<br></br>    for i in range(100):        # 创建100个子线程<br></br>        t=Thread(target=task,args=(lock,))<br></br>        t_li.append(t)<br></br>        t.start()<br></br>    for el in t_li:<br></br>        el.join()               # 循环列表, 每一个线程都必须等待上一个线程执行完,必须等待上一个线程执行完<br></br>    print(f'最后x:{x}')<br></br>​

七丶死锁现象,递归锁

死锁含义(Lock):

一个资源被多次调用,多次调用资源未能释放,会造成一种互相等待的现象.在没有外力作用下,只能停留在这,此时的系统处于锁死状态.

死锁现象(2种):

1.当一个进程或者一个线程一直占调用或者占用同一把锁Lock时,而不释放资源会导致其他进程/线程无法获得锁,就会出现锁死现象.一直出去阻塞acquire()状态

代码见:

<pre class="md-fences md-end-block ty-contain-cm modeLoaded">### 当一个锁对象已经被上锁, 试图再次加锁,  就会造成锁死.<br></br>from multiprocessing import Lock<br></br>​<br></br>def task1(loc):<br></br>    print('task1')<br></br>    loc.acquire()<br></br>    print('task1: 开始打印')<br></br>    time.sleep(random.randint(1,3))<br></br>    print('task1: 结束打印')<br></br>    loc.release()<br></br>​<br></br>def task2(loc):<br></br>    print('task2')<br></br>    loc.acquire()                        # 第一层锁  <br></br>    loc.acquire()                        #第二层锁, 试图再次加锁,由于锁对象已经被占用(已经锁上了,还没有释放)再次上锁,就会造成锁死 (程序被卡主)~~~<br></br>    loc.release()<br></br>    print('task2: 开始打印')<br></br>    time.sleep(random.randint(1,3))<br></br>    print('task2: 结束打印')<br></br>    loc.release()<br></br>​<br></br>​<br></br>def task3(loc):<br></br>    print('task3')<br></br>    loc.acquire()<br></br>    print('task3: 开始打印')<br></br>    time.sleep(random.randint(1,3))<br></br>    print('task3: 结束打印')<br></br>    loc.release()<br></br>​<br></br>​<br></br>if __name__ == '__main__':<br></br>    loc=Lock()<br></br>    p1=Process(target=task1,args=(loc,)).start()<br></br>    p2=Process(target=task2,args=(loc,)).start()<br></br>    p3=Process(target=task3,args=(loc,)).start()<br></br>    

2.当有两个进程/线程同时想要获取两个锁时,由于两者都是处于竞争关系.就可能会出现两者都阻塞在同一放,都无法同时获得两个锁 或者 要获取对方已经获得的还没有释放的锁. 代码如下

<pre class="md-fences md-end-block ty-contain-cm modeLoaded"># -*-coding:utf-8-*-<br></br># Author:Ds<br></br>​<br></br>from threading import  Thread<br></br>from threading import Lock<br></br>import  time<br></br>​<br></br># 两把 不一样的锁<br></br>lock_A=Lock()<br></br>lock_B=Lock()<br></br>​<br></br>class MyThread(Thread):<br></br>​<br></br>    def run(self) -> None:<br></br>        self.f1()<br></br>        self.f2()<br></br>​<br></br>    def f1(self):<br></br>        lock_A.acquire()<br></br>        print(f'{self.name}  拿到A锁')<br></br>​<br></br>        lock_B.acquire() #<br></br>        print(f'{self.name} 那到B锁')<br></br>        lock_B.release()<br></br>​<br></br>        lock_A.release()<br></br>​<br></br>    def f2(self):<br></br>        lock_B.acquire()<br></br>        print(f'{self.name} 拿到B锁')<br></br>​<br></br>        time.sleep(1)<br></br>        lock_A.acquire()<br></br>        print(f'{self.name} 拿到A锁')<br></br>        lock_A.release()<br></br>​<br></br>        lock_B.release()<br></br>​<br></br>if __name__ == '__main__':<br></br>​<br></br>    for i in range(2):   # 实例化两个线程对象<br></br>        t=MyThread()    # 简单命名,线程1和线程2<br></br>        t.start()<br></br>​<br></br>    print('in 主线程')<br></br>​<br></br>    <br></br>### 口述上述代码的过程:<br></br>    #1. 线程 1 执行f1函数, 拿到A锁,    线程2 也要拿A锁.  由于线程1已经拿到A锁,线程2必须等待A锁<br></br>    #2. 线程1  继续执行f1函数 ,此时 B锁没有人使用. 轻松的执行完 f1函数<br></br>    #3. 重点:  线程1执行f2函数 拿走B锁.     而此时A锁已经被释放了,现在线程2拿走A锁,<br></br>    #4. 重点:  线程1执行f2函数 已经拿走B锁,继续执行需要A锁.   此时发现A锁已经被线程2拿走了, 线程2 执行f1函数,已经拿走了A锁,需要B锁.而此时B锁在线程1中.<br></br>    ### 由于竞争关系的存在. 两个线程对同一个资源进行抢夺. 一直卡在acquire() , 造成死锁.

递归锁(RLock):

1.防止线程锁死

2.引用计数原则:非0不能被抢,引用1次锁一次,引用N次锁N次.

3.在并发时,保证数据的安全('串行')

<pre class="md-fences mock-cm md-end-block"># -*-coding:utf-8-*-
# Author:Ds

### 递归锁是一把锁,锁上有记录,只要acquire一次,锁上就计数1次, acquire2次,锁上就计数2次,
# release1次,减一,
# 只要递归锁计数不为0,其他线程不能抢.


from threading import  Thread
from threading import RLock
import  time


# 同一个锁对象,两把一样的锁 ,内部采用单例模式
lock_A=lock_B=RLock()


class MyThread(Thread):

    def run(self) -> None:
        self.f1()
        self.f2()

    def f1(self):
        lock_A.acquire()
        print(f'{self.name}  拿到A锁')

        lock_B.acquire() #
        print(f'{self.name} 那到B锁')
        lock_B.release()

        lock_A.release()

    def f2(self):
        lock_B.acquire()
        print(f'{self.name} 拿到B锁')

        time.sleep(1)
        lock_A.acquire()
        print(f'{self.name} 拿到A锁')
        lock_A.release()

        lock_B.release()

if __name__ == '__main__':

    for i in range(2):   # 实例化两个线程对象
        t=MyThread()
        t.start()

    print('in 主线程')

八丶信号量

含义:

信号量允许多个线程或者进程同时进入

描述:

描述:一个网吧,有三台电脑,一开始三台电脑都没有人.这时候来了5个人要上网,网管允许3个人进入网吧,使用电脑. 剩下的2个人就必须在门外等待,此后来的认也要在门外等待. 如果这时候有1个人已经上完网了,网管得知后,打开网吧的门,让后面的1个人进入网吧上网.如果这时候又有2个人已经上网完毕离开了,网管又打开门,让后面的2个人进入网吧上网.

在网吧系统中,上网电脑: 属于公共资源,上网的人属于一个线程, 网管就是起着信号量的作用

<pre class="md-fences mock-cm md-end-block"># -*-coding:utf-8-*-
# Author:Ds

### 信号量: 同一时间,只允许指定数量的线程工作.(多余的线程排队等候)
### 信号量允许多个线程或者进程同时进入

from  threading import Thread
from  threading import current_thread
from  threading import Semaphore

import time
import random

sm=Semaphore(3) # 定义信号量对象 同一时刻只允许3个任务被处理

def Safe_Internet():
    sm.acquire()

    print(f'{current_thread().name} 正在上网')
    time.sleep(random.randint(1,3)) # 增加随机性
    sm.release()
    print(f'\033[0;35m  {current_thread().name} 已经上完网了 \033[0m')

if __name__ == '__main__':
    t_l=[]
    for i in  range(20):
        t=Thread(target=Safe_Internet,)
        t_l.append(t)
        t.start()

    for i in t_l:
        i.join()

    print('in 主线程~~~~')

标签: 进程, 线程, lock, 死锁, threading, name, print

相关文章推荐

添加新评论,含*的栏目为必填