36 线程 队列 守护线程 互斥锁 死锁 可重入锁 信号量

线程

线程是操作系统最小的运算调度单位,被包含在进程中,一个线程就是一个固定的 执行流程

线程和进程的关系

线程不能单独存在 必须存在于进程中,

进程是一个资源单位,其包含了运行程序所需的所有资源

线程才是真正的执行单位

没有线程,进程中的资源无法被利用起来,所以一个进程至少包含一个线程,称之为主线程

当我们启动一个程序时,操作系统就会自己为这个程序创建一个主线程

线程可以由程序后期开启 ,自己开启线程称之为子线程

为什么需要线程

目的只有一个就是提高效率

就像一个车间 如果产量跟不上 就再造一条流水线

当然可以再造一个新车间,那需要把原材料运过去 ,这个过程是非常耗时的

所以通常情况是创建新的流水线 而不是车间 即 线程

特点:

1.每个进程都会有一个默认的线程

2.每个进程可以存在多个线程

3.同一进程中的所有线程之间数据是共享的

4.创建线程的开销远比创建进程小的多

主线程与子线程的区别:

1.线程之间是没有父子之分,是平等的

2.主线程是由操作系统自动开启的,而子线是由程序主动开启

3.即使主线程的代码执行完毕,也不会结束进程,会等待所有线程执行完毕,进程才结束

JoinableQueue队列from multiprocessing import JoinableQueue
# 可以被join的队列

q = JoinableQueue()

print(‘------------‘)q.put(‘123‘)q.put(‘456‘)

print(‘取走了%s‘% q.get())

q.task_done()# 告诉队列这个数据已经被处理完毕# 而不是表示任务全部处理完成# 只是取出某个数据处理完成print(‘-----------‘)print(‘取走了%s‘% q.get())q.task_done()q.join()#  等待队列中的数据被处理完毕print(‘over‘)

# task_done=put 调用次数相等  进程才会结束

生产者消费者模型
import randomimport timefrom multiprocessing import Process,Queue ,JoinableQueue

def make_ice_cream(name,q):    for i in range(5):        time.sleep(random.randint(1,3))        print("%s生产了冰激凌%s" % (name, i))        q.put(‘%s的%s号冰激凌‘%(name,i))

def eat_ice_cream(name,q):    while True:        ice_cream=q.get()        # if not ice_cream:        #     break        time.sleep(random.randint(1,3))        print(‘%s吃掉了%s‘%(name,ice_cream))        # 必须记录该数据处理完成了        q.task_done()

if __name__ == ‘__main__‘:    q = JoinableQueue()    p1 = Process(target=make_ice_cream,args=(‘阿三的冰激凌店‘,q))    p2 = Process(target=make_ice_cream, args=(‘阿肆的冰激凌店‘, q))    p3 = Process(target=make_ice_cream, args=(‘阿五的冰激凌店‘, q))

    c1 = Process(target=eat_ice_cream,args=(‘大王‘,q))    c1.daemon=True    c2 = Process(target=eat_ice_cream, args=(‘二王‘, q))    c2.daemon=True    c3 = Process(target=eat_ice_cream, args=(‘三王‘, q))    c3.daemon=True

    p1.start()    p2.start()    p3.start()

    c1.start()    c2.start()    c3.start()

    # 目前的思路  是当商家做完以后 放一个None  作为结束标志   而且 必须明确商家和消费者的个数    # 明确商家生成完毕  再明确消费者吃完了  就算结束    p1.join()    print("第一家生成完毕")    p2.join()    print("第二家生成完毕")    p3.join()    print("第三家生成完毕")

    # 消费者吃完了    q.join()    print(‘消费者吃完了‘)
创建线程的俩种方式
from threading import Thread, current_threadimport time

# current_thread:当前线程

def task():    print(‘1‘, current_thread())    print(‘子线程running‘)    time.sleep(5)    print(‘子线程over‘)

# 方法一:直接实例化Thread类if __name__ == ‘__main__‘:    t = Thread(target=task)    t.start()

    task()    print(‘主线程over‘)    print(‘1‘, current_thread())

# 执行顺序不固定 如果开启线程速度足够快  可能子线程先执行# 方法2

class MyThread(Thread):    def run(self):        print(‘子线程run‘)

m = MyThread()m.start()print(‘主线over‘)

# 使用方法和多进程一模一样   开启线程的代码可以放在任何位置  开启进程必须放在判断下面

线程与进程区别:

1.同一进程中 线程之间数据共享

a = 100def task():    global a    print("子线程 run........")    a = 1?t = Thread(target=task)t.start()?print(a) # 1print("over")

2.创建线程的开销远比创建进程小的多

from threading import  Threadfrom multiprocessing import  Processimport time?def task():    pass?if __name__ == ‘__main__‘:    start = time.time()    for i in range(100):        p = Thread(target=task)        p.start()    print(time.time()-start)# 修改Thread 为Process类 查看结果

3.无论开启了多少子线程PID是不会变的

from threading import  Threadimport os?def task():    print(os.getpid())?for i in range(100):    p = Thread(target=task)    p.start()

Tread类的常用属性:

# threading模块包含的常用方法
import threading
print(threading.current_thread().name) #获取当前线程对象
print(threading.active_count()) # 获取目前活跃的线程数量
print(threading.enumerate()) # 获取所有线程对象

t = Thread(name="aaa")
# t.join() # 主线程等待子线程执行完毕
print(t.name) # 线程名称
print(t.is_alive()) # 是否存活
print(t.isDaemon()) # 是否为守护线程

守护线程

一个线程可以设置为另一个线程的守护线程

# 主线程代码执行完毕后,不会立即结束,会等待其他子线程结束# 主线程 会等待非守护线程结束后结束# 如果守护线程已经完成任务,程序立马结束
ef task():    print(‘子线程1 跑........‘)    time.sleep(2)    print(‘子线程1 关.....‘)

def task2():    print(‘子线程2 跑........‘)    time.sleep(2)    print(‘子线程2 关.....‘)

t1=Thread(target=task)t1.daemon=Truet1.start()

t2=Thread(target=task2)t2.start()

print(‘主线程 关.....‘)

顺序是:守护线程 等待 主线程 等待 其余子线程

线程 互斥锁

多线程的最主要特征之一是:同一进程中所有线程数据共享

一旦共享必然出现竞争问题。

线程中也存在安全问题,

多线程可以并发执行,一旦并发了并且访问了同一个资源就会有问题

解决方案:还是互斥锁

案例:

rom threading import Thread, enumerate, Lock

import time

number = 10lock = Lock()

def task():    global number    lock.acquire()    a = number    time.sleep(1)    number = a - 1    lock.release()

for i in range(10):    t = Thread(target=task)    t.start()

for t in enumerate()[1:]:    t.join()

print(number)

死锁问题

死锁问题
当程序出现了不止一把锁,分别被不同的线程持有, 有一个资源 要想使用必须同时具备两把锁
这时候程序就会进程无限卡死状态 ,这就称之为死锁
例如:
要吃饭 必须具备盘子和筷子 但是一个人拿着盘子 等筷子 另一个人拿着筷子等盘子

如何避免死锁问题
锁不要有多个,一个足够
如果真的发生了死锁问题,必须迫使一方先交出锁

例子:

现有两把锁l1和l2 用于表示盘子和筷子

两个线程的目标是吃饭,要吃饭的前提是同时拿到筷子和盘子,但是两个人的目标不同一个先拿筷子 ,一个先拿盘子最终造成死锁

from threading import Lock, current_thread, Thread

# 盘子lock1 = Lock()# 筷子lock2 = Lock()

def eat1():    lock1.acquire()    print("%s抢到了盘子" % current_thread().name)    lock2.acquire()    print("%s抢到了筷子" % current_thread().name)    print("%s开吃了!" % current_thread().name)    lock1.release()    print("%s放下盘子" % current_thread().name)    lock2.release()    print("%s放下筷子" % current_thread().name)

def eat2():    lock2.acquire()    print("%s抢到了筷子" % current_thread().name)    lock1.acquire()    print("%s抢到了盘子" % current_thread().name)    print("%s开吃了!" % current_thread().name)    lock2.release()    print("%s放下筷子" % current_thread().name)    lock1.release()    print("%s放下盘子" % current_thread().name)

t1 = Thread(target=eat1)t2 = Thread(target=eat2)

t1.start()t2.start()

可重入锁

Rlock          称之为递归锁        或者可重入锁    Rlock不使用用来解决死锁问题的

    与Lock唯一的区别:    Rlock同一线程可以多次执行acquire 但是执行几次acquire就应该对应release几次

如果一个线程已经执行过acquire 其他线程将无法执行acquireRlock仅仅是帮你解决了代码逻辑上的错误导致的死锁,并不能解决多个锁造成的死锁问题
正常锁from threading import RLock,Lock,Threadl=Lock()l.acquire()print(‘1111111‘)l.release()print(‘222222222‘)
死锁
from threading import RLock,Lock,Thread
l=Lock()l.acquire()print(‘111111‘)l.acquire()print(‘2222222‘)在处理并发安全时 用完公共资源后一定要释放锁

信号量

Semaphore

信号量也是一种锁,其特殊之处在于可以让一个资源同时被多个线程共享,并控制最大的并发访问线程数量。

Lock 锁住一个马桶  同时只能有一个Semaphore 锁住一个公共厕所    同时可以来一堆人

用途: 仅用于控制并发访问   并不能防止并发修改造成的问题
from threading import Semaphore,Threadimport time

a=Semaphore(5)def task():    a.acquire()    print(‘子线程。。‘)    time.sleep(3)    print(‘主线程。。‘)    a.release()

for i in range(8):    t=Thread(target=task)    t.start()




原文地址:https://www.cnblogs.com/komorebi/p/10976330.html

时间: 2024-12-16 06:34:12

36 线程 队列 守护线程 互斥锁 死锁 可重入锁 信号量的相关文章

Java 中15种锁的介绍:公平锁,可重入锁,独享锁,互斥锁,乐观锁,分段锁,自旋锁等等(转)

Java 中15种锁的介绍 在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类.介绍的内容如下: 公平锁 / 非公平锁 可重入锁 / 不可重入锁 独享锁 / 共享锁 互斥锁 / 读写锁 乐观锁 / 悲观锁 分段锁 偏向锁 / 轻量级锁 / 重量级锁 自旋锁 上面是很多锁的名词,这些分类并不是全是指锁的状态,有的指锁的特性,有的指锁的设计,下面总结的内容是对每个锁的名词进行一定的解释. 公平锁 / 非公平锁 公平锁 公平锁是指多个线程按照申请锁的顺序来获取锁. 非公

通俗易懂 悲观锁、乐观锁、可重入锁、自旋锁、偏向锁、轻量/重量级锁、读写锁、各种锁及其Java实现!

网上关于Java中锁的话题可以说资料相当丰富,但相关内容总感觉是一大串术语的罗列,让人云里雾里,读完就忘.本文希望能为Java新人做一篇通俗易懂的整合,旨在消除对各种各样锁的术语的恐惧感,对每种锁的底层实现浅尝辄止,但是在需要时能够知道去查什么. 首先要打消一种想法,就是一个锁只能属于一种分类.其实并不是这样,比如一个锁可以同时是悲观锁.可重入锁.公平锁.可中断锁等等,就像一个人可以是男人.医生.健身爱好者.游戏玩家,这并不矛盾.OK,国际惯例,上干货. 〇.synchronized与Lock

写文章 通俗易懂 悲观锁、乐观锁、可重入锁、自旋锁、偏向锁、轻量/重量级锁、读写锁、各种锁及其Java实现!

网上关于Java中锁的话题可以说资料相当丰富,但相关内容总感觉是一大串术语的罗列,让人云里雾里,读完就忘.本文希望能为Java新人做一篇通俗易懂的整合,旨在消除对各种各样锁的术语的恐惧感,对每种锁的底层实现浅尝辄止,但是在需要时能够知道去查什么. 首先要打消一种想法,就是一个锁只能属于一种分类.其实并不是这样,比如一个锁可以同时是悲观锁.可重入锁.公平锁.可中断锁等等,就像一个人可以是男人.医生.健身爱好者.游戏玩家,这并不矛盾.OK,国际惯例,上干货. 〇.synchronized与Lock

可重入锁和不可重入锁

锁的简单应用 用lock来保证原子性(this.count++这段代码称为临界区) 什么是原子性,就是不可分,从头执行到尾,不能被其他线程同时执行. 可通过CAS来实现原子操作 CAS(Compare and Swap): CAS操作需要输入两个数值,一个旧值(期望操作前的值)和一个新值,在操作期间先比较下旧值有没有发生变化,如果没有发生变化,才交换成新值,发生了变化则不交换. CAS主要通过compareAndSwapXXX()方法来实现,而这个方法的实现需要涉及底层的unsafe类 unsa

【分布式锁】06-Zookeeper实现分布式锁:可重入锁源码分析

前言 前面已经讲解了Redis的客户端Redission是怎么实现分布式锁的,大多都深入到源码级别. 在分布式系统中,常见的分布式锁实现方案还有Zookeeper,接下来会深入研究Zookeeper是如何来实现分布式锁的. Zookeeper初识 文件系统 Zookeeper维护一个类似文件系统的数据结构 image.png 每个子目录项如NameService都被称为znoed,和文件系统一样,我们能够自由的增加.删除znode,在znode下增加.删除子znode,唯一不同的在于znode是

锁 Lock、重入锁、写入锁

ReentrantLock 重入锁 类似于synchronize 区别与写法上,在需要进行同步的代码部分加上锁定,但不要忘记最后一定要释放锁定, 不然会造成锁永远无法释放,其他线程永远进不来的结果.eg: 1 package com.zym.height.Lock01; 2 3 import java.util.concurrent.locks.Lock; 4 import java.util.concurrent.locks.ReentrantLock; 5 6 public class Us

zbb20180929 thread 自旋锁、阻塞锁、可重入锁、悲观锁、乐观锁、读写锁、对象锁和类锁

1.自旋锁自旋锁可以使线程在没有取得锁的时候,不被挂起,而转去执行一个空循环,(即所谓的自旋,就是自己执行空循环),若在若干个空循环后,线程如果可以获得锁,则继续执行.若线程依然不能获得锁,才会被挂起.使用自旋锁后,线程被挂起的几率相对减少,线程执行的连贯性相对加强.因此,对于那些锁竞争不是很激烈,锁占用时间很短的并发线程,具有一定的积极意义,但对于锁竞争激烈,单线程锁占用很长时间的并发程序,自旋锁在自旋等待后,往往毅然无法获得对应的锁,不仅仅白白浪费了CPU时间,最终还是免不了被挂起的操作 ,

Java锁之可重入锁介绍

锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronized 和 ReentrantLock等等 ) .这些已经写好提供的锁为我们开发提供了便利,但是锁的具体性质以及类型却很少被提及.本系列文章将分析JAVA下常见的锁名称以及特性,为大家答疑解惑. 四.可重入锁: 本文里面讲的是广义上的可重入锁,而不是单指JAVA下的ReentrantLock. 可重入锁,也叫做递归锁,指的是同一线程 外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码,但不受影响. 在JAV

Java并发程序设计(12)并发锁之可重入锁ReentrantLock

1.1. 可重入锁ReentrantLock ReentrantLock是java并发库中提供的可重入锁.与synchronized同步块相比,有相似也有不同.相似的地方有: (1)都可以实现多线程之间的同步,避免对共享资源的访问冲突. (2)都是可重入的,即一个已经获取锁的线程可以再次获得同一个锁,synchronized也类似. 不同的地方有: (1)ReentrantLock更灵活,获取锁和释放锁可以在同一个方法中,也可以在不同方法中.synchronized通常用在同一个方法体内. (2