Python中线程同步与线程锁 你的名字 2022-01-21 10:39 345阅读 0赞 ### 文章目录 ### * 线程同步与线程锁 * * 线程同步 * 1.threading.Event对象 * 2.threading.Timer定时器,延迟执行 * 3.threading.Lock锁 * 4.可重入锁RLock * 5.Condition条件锁,等待通知 * 6.therading.Semaphore信号量 * 7.threading.BoundedSemaphore有界信号量 * 总结 # 线程同步与线程锁 # ## 线程同步 ## **概念** \* 线程同步,线程间协同,通过某种技术,让一个线程访问某些数据时,其他线程不能访问这些数据,直到该线程完 成对数据的操作。 ## 1.threading.Event对象 ## * Event事件,是线程间通信机制中最简单的实现,使用一个内部的标记flag,通过flag的True或False的变化 来进行操作 <table> <thead> <tr> <th align="left">名称</th> <th align="left">含义</th> </tr> </thead> <tbody> <tr> <td align="left">event.set()</td> <td align="left">标记设置为True</td> </tr> <tr> <td align="left">event.clear()</td> <td align="left">标记设置为False</td> </tr> <tr> <td align="left">event.is_set()</td> <td align="left">标记是否为True</td> </tr> <tr> <td align="left">event.wait(timeout=None)</td> <td align="left">设置等待标记为True的时长,None为无限等待。等到返回True,未等到超时了返回False</td> </tr> </tbody> </table> * 老板雇佣了一个工人,让他生产杯子,老板一直等着这个工人,直到生产了10个杯子 import threading import time import logging logging.basicConfig(format="%(asctime)s %(threadName)s %(thread)s %(message)s",level=logging.INFO) def worker(event:threading.Event,count = 10): logging.info("我是worker工作线程") cups = [] while True: logging.info("制作了一个 cup") time.sleep(0.2) cups.append(1) if len(cups)>=count: event.set() break logging.info("制作完成:{}".format(cups)) def boss(event:threading.Event): logging.info("我是boss") event.wait() logging.info("Good Job") event = threading.Event() b = threading.Thread(target=boss,args=(event,)) w = threading.Thread(target=worker,args=(event,)) b.start() w.start() ![threading2\_001][threading2_001] * 使用同一个Event对象的标记flag。 * 谁wait就是等到flag变为True,或等到超时返回False。不限制等待的个数。 * wait的使用 from threading import Thread,Event import logging logging.basicConfig(format="%(asctime)s %(threadName)s %(thread)s %(message)s",level=logging.INFO) def worker(event:Event,interval:int): while not event.wait(interval): logging.info("没有等到。。") e = Event() Thread(target=worker,args=(e,1)).start() e.wait(5) e.set() print("======end========") ![threading2\_002][threading2_002] ## 2.threading.Timer定时器,延迟执行 ## <table> <thead> <tr> <th align="left">方法</th> <th align="left">含义</th> </tr> </thead> <tbody> <tr> <td align="left">Timer.cancel()</td> <td align="left">取消定时器,(定时器为执行函数时可以取消,在函数执行中无法取消)</td> </tr> <tr> <td align="left">Time.start()</td> <td align="left">启动定时器</td> </tr> </tbody> </table> * threading.Timer继承自Thread,这个类用来定义延迟多久后执行一个函数。 * `class threading.Timer(interval, function, args=None, kwargs=None)` 1. interval \#多少时间后执行function函数 2. function \#需要执行的函数 * start方法执行之后,Timer对象会处于等待状态,等待了interval秒之后,开始执行function函数的。 * Timer是线程Thread的子类,Timer实例内部提供了一个finished属性,该属性是Event对象。 * cancel方法,本质上 是在worker函数执行前对finished属性set方法操作,从而跳过了worker函数执行,达到了取消的效果。 from threading import Timer import logging import time logging.basicConfig(format="%(asctime)s %(threadName)s %(thread)s %(message)s",level=logging.INFO) def worker(): logging.info("in worker") time.sleep(5) logging.info("end in worker") t = Timer(2,worker) t.setName("timer1") #设置线程名称 # t.cancel() #取消定时器后,定时器不在执行 t.start() # t.cancel() #取消定时器后,定时器不在执行 time.sleep(4) #等待4秒后,定时器已经开始执行 t.cancel() #当定时器执行后,无法取消 print("======end========") ![threading2\_003][threading2_003] ## 3.threading.Lock锁 ## 锁(Lock):一旦线程获得锁,其他试图获取锁的线程将被阻塞等待。 锁:凡是存在共享支援争抢的地方都可以使用锁,从而保证只有一个使用者可以完全使用这个资源。 <table> <thead> <tr> <th align="left">名称</th> <th align="left">含义</th> </tr> </thead> <tbody> <tr> <td align="left">Lock.acquire(blocking=True,timeout=-1)</td> <td align="left">获取锁,获取成功返回True,否则返回False<br>当获取不到锁时,默认进入阻塞状态,直到获取到锁,后才继续。阻塞可以设置超时时间。非阻塞时,timeout禁止设置。如果超时依旧未获取到锁,返回False。</td> </tr> <tr> <td align="left">Lock.rease()</td> <td align="left">释放锁,可以从任何线程调用释放。<br>已上锁的锁,会被设置为unlocked。如果未上锁调用,会抛出RuntimeError异常。</td> </tr> </tbody> </table> import threading import sys import time def print(*args): sys.stdout.write(" ".join(map(str,args))+"\n") def worker(lock): print("worker start",threading.get_ident(),threading.current_thread().name) lock.acquire() print("worker over",threading.get_ident(),threading.current_thread().name) lock = threading.Lock() lock.acquire() print(" -"*30) for i in range(5): threading.Thread(target=worker,args=(lock,),name="w{}".format(i)).start() print("- "* 30) while True: time.sleep(0.1) cmd = input(">>").strip() if cmd == "r": lock.release() elif cmd == "q": break else: print(threading.enumerate()) ![threading2\_004][threading2_004] 上例可以看出不管在哪一个线程中,只要对一个已经上锁的锁阻塞请求,该线程就会阻塞。 * **加锁,解锁** 一般来说,加锁就需要解锁,但是加锁后解锁前,还要有一些代码执行,就有可能抛异常,一旦出现异常,锁是无 法释放,但是当前线程可能因为这个异常被终止了,这也产生了死锁 * 加锁、解锁常用语句: 1. 使用try…finally语句保证锁的释放 2. with上下文管理,锁对象支持上下文管理 * 计数器类,可加,可减。 import threading import sys import time def print(*args): sys.stdout.write(" ".join(map(str,args))+"\n") class Counter: def __init__(self): self._val = 0 self.lock = threading.Lock() @property def value(self): with self.lock: return self._val def inc(self): with self.lock: self._val += 1 def dec(self): with self.lock: self._val -= 1 def run(c:Counter,count=100): for _ in range(count): for i in range(-50,50): if i <0: c.dec() else: c.inc() c = Counter() c1 = 10 #线程数 c2 = 1000 for i in range(c1): threading.Thread(target=run,args=(c,c2)).start() for k in threading.enumerate(): if k.name != "MainThread": k.join() print(c.value) * **锁的应用场景** 锁适用于访问和修改同一个共享资源的时候,即读写同一个资源的时候。 * 使用锁的注意事项: 1. 少用锁,必要时用锁。使用了锁,多线程访问被锁的资源时,就成了串行,要么排队执行,要么争抢执行 * 举例,高速公路上车并行跑,可是到了省界只开放了一个收费口,过了这个口,车辆依然可以在多车道 上一起跑。过收费口的时候,如果排队一辆辆过,加不加锁一样效率相当,但是一旦出现争抢,就必须 加锁一辆辆过。注意,不管加不加锁,只要是一辆辆过,效率就下降了。 2. 加锁时间越短越好,不需要就立即释放锁 3. 一定要避免死锁 * **非阻塞锁的使用** import threading import sys import time def print(*args): sys.stdout.write(" ".join(map(str,args))+"\n") def worker(lock:threading.Lock): while True: if lock.acquire(False): print("do something.") time.sleep(1) lock.release() break else: print("try again") time.sleep(1) lock = threading.Lock() for i in range(5): threading.Thread(target=worker,name="w{}".format(i),args=(lock,)).start() ![threading2\_005][threading2_005] ## 4.可重入锁RLock ## * 可重入锁,是**线程相关**的锁 * 线程A获得可重复锁,并可以多次成功获取,不会阻塞。最后要在线程A中做和acquire次数相同的release import threading import sys import time def print(*args): sys.stdout.write(" ".join(map(str,args))+"\n") def fib(num,rlock:threading.RLock): with rlock: if num<3: return 1 return fib(num-1,rlock)+fib(num-2,rlock) def work(num,rlock): print(fib(num,rlock)) rlock = threading.RLock() for i in range(1,10): threading.Thread(target=work,args=(i,rlock)).start() 可重入锁 \* 与线程相关,可在一个线程中获取锁,并可继续在同一线程中不阻塞多次获取锁 \* 当锁未释放完,其它线程获取锁就会阻塞,直到当前持有锁的线程释放完锁 \* 锁都应该使用完后释放。可重入锁也是锁,应该acquire多少次,就release多少次 ## 5.Condition条件锁,等待通知 ## 构造方法Condition(lock=None),可以传入一个Lock或RLock对象,默认是RLock。 <table> <thead> <tr> <th align="left">名称</th> <th align="left">含义</th> </tr> </thead> <tbody> <tr> <td align="left">Condition.acquire(self,*args)</td> <td align="left">获取锁</td> </tr> <tr> <td align="left">Condition.wait(self,timeout=None)</td> <td align="left">等待通知,timeout设置超时时间</td> </tr> <tr> <td align="left">Condition.notify(self,n=1)</td> <td align="left">唤醒至多指定数目个数的等待的线程,没有等待的线程就没有任何操作</td> </tr> <tr> <td align="left">Condition.notify_all(self)</td> <td align="left">唤醒所有等待的线程</td> </tr> </tbody> </table> * 每个线程都可以通过Condition获取已把属于自己的锁,在锁中可以等待其他进程的同级锁的通知。当获取到同级锁的通知后,会停止等待。 * 当使用Condition(lock=Lock())初始化锁时,锁只能一级等待,不能出现多级等待。 * 简单示例: import threading import time def work(cond:threading.Condition): with cond: print("开始等待") cond.wait() print("等到了") cond = threading.Condition() # cond = threading.Condition(threading.Lock()) threading.Thread(target=work,args=(cond,)).start() threading.Thread(target=work,args=(cond,)).start() with cond: with cond: time.sleep(1) print("开始释放二级等待") print(cond.notifyAll()) time.sleep(2) print("开始释放一级等待") cond.notifyAll() ![threading2\_006][threading2_006] * 广播模式示例: from threading import Thread,Condition,Lock import time import logging import random logging.basicConfig(format="%(asctime)s %(threadName)s %(thread)s %(message)s",level=logging.INFO) class Dispachter: def __init__(self): self.data = None self.cond = Condition(lock=Lock()) #生成者 def produce(self,total): for _ in range(total): data = random.randint(1,100) with self.cond: logging.info("生产了一个数据:{}".format(data)) self.data = data self.cond.notify(1) time.sleep(1) #模拟生成数据需要耗时1秒 #消费者 def consume(self): while True: with self.cond: self.cond.wait() #等待 data = self.data logging.info("消费了一个数据 {}".format(data)) self.data = None d = Dispachter() p = Thread(target=d.produce,name="producer",args=(10,)) # 增加消费者 for i in range(5): c = Thread(target=d.consume,name="consumer{}".format(i)) c.start() p.start() ![threading2\_007][threading2_007] 上面例子中演示了**生产者**生产一个数据,就通知一个消费者消费。 * **Condition总结** Condition用于生产者消费者模型中,解决生产者消费者速度匹配的问题。采用了通知机制,非常有效率。 * 使用方式 使用Condition,必须先acquire,用完了要release,因为内部使用了锁,默认使用RLock锁,最好的方式是使用 with上下文。 消费者wait,等待通知。 生产者生产好消息,对消费者发通知,可以使用notify或者notify\_all方法。 ## 6.therading.Semaphore信号量 ## 和Lock很像,信号量对象内部维护一个倒计数器,每一次acquire都会减1,当acquire方法发现计数为0就阻塞请求 的线程,直到其它线程对信号量release后,计数大于0,恢复阻塞的线程。 <table> <thead> <tr> <th align="left">名称</th> <th align="left">含义</th> </tr> </thead> <tbody> <tr> <td align="left">Semaphore(value=1)</td> <td align="left">构造方法。value为初始信号量。value小于0,抛出ValueError异常</td> </tr> <tr> <td align="left">Semaphore.acquire(self,blocking=True,timeout=None)</td> <td align="left">获取信号量,技术器减1,即_value的值减少1。如果_value的值为0会变成阻塞状态。获取成功返回True</td> </tr> <tr> <td align="left">Semaphore.release(self)</td> <td align="left">释放信号量,计数器加1。即_value的值加1</td> </tr> <tr> <td align="left">Semaphore._value</td> <td align="left">信号量,当前信号量</td> </tr> </tbody> </table> * **注意**: 1. 计数器永远不会低于0,因为acquire的时候,发现是0,都会被阻塞。 2. 信号量没有做超界限制 from threading import Semaphore s =Semaphore(3) print(s._value) s.release() #会增加信号量 print(s._value) #可以看出没有做信号量上线控制 print("----------------") print(s.acquire()) print(s._value) print(s.acquire()) print(s._value) print(s.acquire()) print(s._value) print(s.acquire()) print(s._value) print(s.acquire()) #当信号量为0再次acquire会被阻塞 print("~~~~~~阻塞了吗?") print(s._value) ![threading2\_008][threading2_008] * 跨线程使用演示 from threading import Thread,Semaphore import time import logging logging.basicConfig(format="%(asctime)s %(threadName)s %(thread)s %(message)s",level=logging.INFO) #定义获取信号量 def worker(s:Semaphore): while s.acquire(): logging.info("被执行了一次,获取一个信号量 _value={}".format(s._value)) #释放信号量 def cunn(s:Semaphore): while True: logging.info("释放一个信号量") s.release() time.sleep(1) s = Semaphore(3) #创建3个线程获取信号量 for i in range(3): Thread(target=worker,args=(s,),name="w{}".format(i)).start() #开启一个线程释放信号量 Thread(target=cunn,args=(s,)).start() ![threading2\_009][threading2_009] ## 7.threading.BoundedSemaphore有界信号量 ## * 有界信号量,不允许使用release超出初始值的范围,否则,抛出ValueError异常 <table> <thead> <tr> <th align="left">名称</th> <th align="left">含义</th> </tr> </thead> <tbody> <tr> <td align="left">BoundedSemaphore(value=1)</td> <td align="left">构造方法。value为初始信号量。value小于0,抛出ValueError异常</td> </tr> <tr> <td align="left">BoundedSemaphore.acquire(self,blocking=True,timeout=None)</td> <td align="left">获取信号量,技术器减1,即_value的值减少1。如果_value的值为0会变成阻塞状态。获取成功返回True</td> </tr> <tr> <td align="left">BoundedSemaphore.release(self)</td> <td align="left">释放信号量,计数器加1。即_value的值加1,超过初始化值会抛出异常ValueError。</td> </tr> <tr> <td align="left">BoundedSemaphore._value</td> <td align="left">信号量,当前信号量</td> </tr> </tbody> </table> from threading import BoundedSemaphore bs = BoundedSemaphore(3) print(bs._value) bs.acquire() bs.acquire() bs.acquire() print(bs._value) bs.release() bs.release() bs.release() print(bs._value) bs.release() ![threading2\_010][threading2_010] * 应用举例 连接池 因为资源有限,且开启一个连接成本高,所以,使用连接池。 一个简单的连接池 连接池应该有容量(总数),有一个工厂方法可以获取连接,能够把不用的连接返回,供其他调用者使用。 from threading import Thread,BoundedSemaphore import time import logging import random logging.basicConfig(format="%(asctime)s %(threadName)s %(thread)s %(message)s",level=logging.INFO) #链接类 class Conn: def __init__(self,name): self.name = name class Pool: def __init__(self,count:int): self.count = count #池中放着链接备用 self.pool = [self._connect("conn-{}".format(i)) for i in range(count)] self.bsemaphore = BoundedSemaphore(count) #创建连接方法,返回一个连接对象 def _connect(self,conn_name): return Conn(conn_name) #获取一个链接 def get_conn(self): self.bsemaphore.acquire() self.pool.pop() logging.info("从连接池拿走了一个连接~~~~~~~") #归还一个连接 def return_conn(self,conn:Conn): logging.info("归还了一个连接----------") self.pool.append(conn) self.bsemaphore.release() def worker(pool:Pool): conn = pool.get_conn() logging.info(conn) #模拟使用了一段时间 time.sleep(random.randint(1,5)) pool.return_conn(conn) pool = Pool(3) for i in range(6): Thread(target=worker,name="worker-{}".format(i),args=(pool,)).start() ![threading2\_011][threading2_011] 上例中,使用信号量解决资源有限的问题。 如果池中有资源,请求者获取资源时信号量减1,拿走资源。当请求超过资源数,请求者只能等待。当使用者用完 归还资源后信号量加1,等待线程就可以被唤醒拿走资源。 注意:这个连接池的例子不能用到生成环境,只是为了说明信号量使用的例子,连接池还有很多未完成功能。 * **问题分析** 1. 边界问题 * 假设一种极端情况,计数器还差1就归还满了,有三个线程A、B、C都执行了第一句,都没有来得及release,这时 候轮到线程A release,正常的release,然后轮到线程C先release,一定出问题,超界了,直接抛异常。 因此信号量,可以保证,一定不能多归还。 2. 正常使用分析 * 正常使用信号量,都会先获取信号量,然后用完归还。 * 创建很多线程,都去获取信号量,没有获得信号量的线程都阻塞。能归还的线程都是前面获取到信号量的线程,其 他没有获得线程都阻塞着。非阻塞的线程append后才release,这时候等待的线程被唤醒,才能pop,也就是没有 获取信号量就不能pop,这是安全的。 * 经过上面的分析,信号量比计算列表长度好,线程安全。 * **信号量和锁** 1. 信号量,可以多个线程访问共享资源,但这个共享资源数量有限。 2. 锁,可以看做特殊的信号量,即信号量计数器初值为1。只允许同一个时间一个线程独占资源。 ## 总结 ## <table> <tbody> <tr> <td colspan="3"> <h4>threading模块中的类</h4> </td> </tr> <tr> <td>类</td> <td>常用方法</td> <td>含义</td> </tr> <tr> <td>Event</td> <td align="left">set(self)</td> <td align="left">将标记设置为True</td> </tr> <tr> <td align="left">clear(self)</td> <td align="left">将标记设置为False</td> </tr> <tr> <td align="left">is_set()</td> <td align="left">判断当前标记是否为True,是True返回True,否则返回False<br>相当于为返回当前标记</td> </tr> <tr> <td align="left">wait(self,timeout=None)</td> <td align="left">如果当前标记为True,立即返回True,如果当前标记为False,会产生一个阻塞,直到标记为True时返回True。timeout等待超时时间,默认为None表示无限等待。未等到超时了返回False</td> </tr> <tr> <td>Time定时器,延迟执行</td> <td align="left">Timer(interval, function, args=None, kwargs=None)</td> <td align="left"> 实例化构造方法<br> 1. interval #多少时间后执行function函数<br> 2. function #需要执行的函数 </td> </tr> <tr> <td align="left">cancel(self)</td> <td align="left">取消定时器<br>(定时器为执行函数时可以取消,在函数执行中无法取消)</td> </tr> <tr> <td align="left">start()</td> <td align="left">启动定时器</td> </tr> <tr> <td>Lock锁</td> <td align="left">acquire(self,blocking=True,timeout=-1)</td> <td align="left">获取锁,获取成功返回True,否则返回False<br>当获取不到锁时,默认进入阻塞状态,直到获取到锁,后才继续。<br>阻塞可以设置超时时间。<br>非阻塞时,timeout禁止设置。如果超时依旧未获取到锁,返回False。</td> </tr> <tr> <td align="left">rease(self)</td> <td align="left">释放锁,可以从任何线程调用释放。<br>已上锁的锁,会被设置为unlocked。如果未上锁调用,会抛出RuntimeError异常。</td> </tr> <tr> <td>RLock可重入锁</td> <td align="left" colspan="2">和Lock类似但是:<br>线程A获得可重复锁,并可以多次成功获取,不会阻塞。最后要在线程A中做和acquire次数相同的release </td> </tr> <tr> <td>Condition</td> <td align="left">Condition(self,lock=None)</td> <td align="left">构造方法,lock默认值为None表示使用RLock()锁,也可以自己传入为Lock()</td> </tr> <tr> <td align="left">acquire(self,*args)</td> <td align="left">获取锁</td> </tr> <tr> <td align="left">rease(self)</td> <td align="left">释放锁</td> </tr> <tr> <td align="left">wait(self,timeout=None)</td> <td align="left">等待通知,timeout设置超时时间。<br>注意:必须获取锁后才能等待通知,notify或notify_all可以发通知</td> </tr> <tr> <td align="left">notify(self,n=1)</td> <td align="left">唤醒至多指定数目个数的等待的线程,没有等待的线程就没有任何操作</td> </tr> <tr> <td align="left">notify_all(self)</td> <td align="left">唤醒所有等待的线程</td> </tr> <tr> <td>Semaphore信号量</td> <td align="left">Semaphore(value=1)</td> <td align="left">构造方法。value为初始信号量。value小于0,抛出ValueError异常</td> </tr> <tr> <td align="left">acquire(self,blocking=True,timeout=None)</td> <td align="left">获取信号量,技术器减1,即_value的值减少1。<br>如果_value的值为0会变成阻塞状态。获取成功返回True</td> </tr> <tr> <td align="left">release(self)</td> <td align="left">释放信号量,计数器加1。即_value的值加1</td> </tr> <tr> <td align="left">`_value`属性</td> <td align="left">信号量,当前信号量</td> </tr> <tr> <td>BoundedSemaphore有界信号量</td> <td align="left">BoundedSemaphore(value=1)</td> <td align="left">构造方法。value为初始信号量。value小于0,抛出ValueError异常</td> </tr> <tr> <td align="left">acquire(self,blocking=True,timeout=None)</td> <td align="left">获取信号量,技术器减1,即_value的值减少1。<br>如果_value的值为0会变成阻塞状态。获取成功返回True</td> </tr> <tr> <td align="left">release(self)</td> <td align="left">释放信号量,计数器加1。即_value的值加1,超过初始化值会抛出异常ValueError。</td> </tr> <tr> <td align="left">_value</td> <td align="left">信号量,当前信号量</td> </tr> </tbody> </table> [threading2_001]: /images/20220121/bfc677f632714033ac86e1edc6c7f951.png [threading2_002]: /images/20220121/a88c62ee084f401c9f536bbef6420fe7.png [threading2_003]: /images/20220121/6a1054c0a9634123adcfa5b46013f6d9.png [threading2_004]: /images/20220121/f7643254aeff4689bb3c2bd3e3141c07.png [threading2_005]: /images/20220121/3ddfaabc7379418789faab192cef0479.png [threading2_006]: /images/20220121/65eca749bbf1414d896fd52522cb6031.png [threading2_007]: /images/20220121/bea0732030d54e728ee2e96ef618fa17.png [threading2_008]: /images/20220121/17b106c20af14a78a16094e6ef5865ec.png [threading2_009]: /images/20220121/c911b8b30c234e3ca5c449e44e12825c.png [threading2_010]: /images/20220121/c3aebd29851b4015bf945cef22cf5cc0.png [threading2_011]: /images/20220121/eb82c108035341439baec492a2903566.png
相关 Java中线程同步问题:死锁示例 在Java中,死锁是一种线程同步问题。当两个或更多的线程相互等待对方释放资源时,就会出现死锁。 以下是一个经典的Java死锁示例: ```java // 定义两个互斥的资源 柔情只为你懂/ 2024年09月11日 08:09/ 0 赞/ 16 阅读
相关 线程同步、线程锁、线程通信 > [Java知识点总结:想看的可以从这里进入][Java] 目录 9.5、线程同步 9.5.1、synchronized 末蓝、/ 2024年03月27日 14:13/ 0 赞/ 32 阅读
相关 Java线程:线程的同步与锁 本文转载至:[http://lavasoft.blog.51cto.com/62575/99155][http_lavasoft.blog.51cto.com_62575_99 爱被打了一巴掌/ 2024年02月17日 21:13/ 0 赞/ 35 阅读
相关 java线程(二):线程同步与同步锁 在前一篇中已经介绍了如何创建线程以及对线程的五种状态有了基本的认识。本次主要分析线程中一个重要的问题线程同步以及如何同步。 为什么要对线程进行同步? 线程有可能和 傷城~/ 2022年09月22日 11:57/ 0 赞/ 267 阅读
相关 Java线程:线程的同步与锁 Java线程:线程的同步与锁 一、同步问题提出 线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏。 例如:两个线程ThreadA 一时失言乱红尘/ 2022年08月24日 01:55/ 0 赞/ 88 阅读
相关 Java中线程同步 / Created by cuboo on 2016/10/3. / public class threaddemo { 水深无声/ 2022年07月16日 08:50/ 0 赞/ 238 阅读
相关 Java线程同步与锁 一、多线程同步 当AB两个线程同时竞争资源时,A线程先获取到资源执行,B线程处于阻塞状态,A线程释放资源后,B线程从挂起处继续执行。 测试代码: public c - 日理万妓/ 2022年07月14日 00:23/ 0 赞/ 287 阅读
相关 Python中线程同步与线程锁 文章目录 线程同步与线程锁 线程同步 1.threading.Event对象 2.threading.Timer定时器,延迟 你的名字/ 2022年01月21日 10:39/ 0 赞/ 346 阅读
相关 Java 进程/线程与线程同步/死锁 一、进程与线程概念 1、并发和并行是即相似又有区别: 并发:指两个或多个事件在同一时间段内发生。 并行:指两个或多个事件在同一时刻点发 左手的ㄟ右手/ 2021年11月27日 05:12/ 0 赞/ 430 阅读
还没有评论,来说两句吧...