0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Python中最常用的几种线程锁你会用吗

马哥Linux运维 来源:博客园 作者:云崖君 2021-10-11 16:07 次阅读

前言本文将继续围绕 threading 模块讲解,基本上是纯理论偏多。

对于日常开发者来讲很少会使用到本文的内容,但是对框架作者等是必备知识,同时也是高频的面试常见问题。

线程安全线程安全是多线程或多进程编程中的一个概念,在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。

线程安全的问题最主要还是由线程切换导致的,比如一个房间(进程)中有10颗糖(资源),除此之外还有3个小人(1个主线程、2个子线程),当小人A吃了3颗糖后被系统强制进行休息时他认为还剩下7颗糖,而当小人B工作后又吃掉了3颗糖,那么当小人A重新上岗时会认为糖还剩下7颗,但是实际上只有4颗了。

上述例子中线程A和线程B的数据不同步,这就是线程安全问题,它可能导致非常严重的意外情况发生,我们按下面这个示例来进行说明。

下面有一个数值num初始值为0,我们开启2条线程:

线程1对num进行一千万次+1的操作

线程2对num进行一千万次-1的操作

结果可能会令人咋舌,num最后并不是我们所想象的结果0:

importthreading

num=0


defadd():
globalnum
foriinrange(10_000_000):
num+=1


defsub():
globalnum
foriinrange(10_000_000):
num-=1


if__name__=="__main__":
subThread01=threading.Thread(target=add)
subThread02=threading.Thread(target=sub)

subThread01.start()
subThread02.start()

subThread01.join()
subThread02.join()

print("numresult:%s"%num)

#结果三次采集
#numresult:669214
#numresult:-1849179
#numresult:-525674

上面这就是一个非常好的案例,想要解决这个问题就必须通过锁来保障线程切换的时机。

需要我们值得留意的是,在Python基本数据类型中list、tuple、dict本身就是属于线程安全的,所以如果有多个线程对这3种容器做操作时,我们不必考虑线程安全问题。

锁的作用锁是Python提供给我们能够自行操控线程切换的一种手段,使用锁可以让线程的切换变的有序。

一旦线程的切换变的有序后,各个线程之间对数据的访问、修改就变的可控,所以若要保证线程安全,就必须使用锁。

threading模块中提供了5种最常见的锁,下面是按照功能进行划分:

同步锁:lock(一次只能放行一个)

递归锁:rlock(一次只能放行一个)

条件锁:condition(一次可以放行任意个)

事件锁:event(一次全部放行)

信号量锁:semaphore(一次可以放行特定个)

1、Lock() 同步锁基本介绍

Lock锁的称呼有很多,如:

同步锁

互斥锁

它们是什么意思呢?如下所示:

互斥指的是某一资源同一时刻仅能有一个访问者对其进行访问,具有唯一性和排他性,但是互斥无法限制访问者对资源的访问顺序,即访问是无序的

同步是指在互斥的基础上(大多数情况),通过其他机制实现访问者对资源的有序访问

同步其实已经实现了互斥,是互斥的一种更为复杂的实现,因为它在互斥的基础上实现了有序访问的特点

下面是threading模块与同步锁提供的相关方法:

方法 描述
threading.Lock() 返回一个同步锁对象
lockObject.acquire(blocking=True, timeout=1) 上锁,当一个线程在执行被上锁代码块时,将不允许切换到其他线程运行,默认锁失效时间为1秒
lockObject.release() 解锁,当一个线程在执行未被上锁代码块时,将允许系统根据策略自行切换到其他线程中运行
lockObject.locaked() 判断该锁对象是否处于上锁状态,返回一个布尔值

使用方式

同步锁一次只能放行一个线程,一个被加锁的线程在运行时不会将执行权交出去,只有当该线程被解锁时才会将执行权通过系统调度交由其他线程。

如下所示,使用同步锁解决最上面的问题:

importthreading

num=0


defadd():
lock.acquire()
globalnum
foriinrange(10_000_000):
num+=1
lock.release()


defsub():
lock.acquire()
globalnum
foriinrange(10_000_000):
num-=1
lock.release()

if__name__=="__main__":
lock=threading.Lock()

subThread01=threading.Thread(target=add)
subThread02=threading.Thread(target=sub)

subThread01.start()
subThread02.start()

subThread01.join()
subThread02.join()

print("numresult:%s"%num)

#结果三次采集
#numresult:0
#numresult:0
#numresult:0

这样这个代码就完全变成了串行的状态,对于这种计算密集型I/O业务来说,还不如直接使用串行化单线程执行来得快,所以这个例子仅作为一个示例,不能概述锁真正的用途。

死锁现象

对于同步锁来说,一次acquire()必须对应一次release(),不能出现连续重复使用多次acquire()后再重复使用多次release()的操作,这样会引起死锁造成程序的阻塞,完全不动了,如下所示:

importthreading

num=0


defadd():
lock.acquire()#上锁
lock.acquire()#死锁
#不执行
globalnum
foriinrange(10_000_000):
num+=1
lock.release()
lock.release()


defsub():
lock.acquire()#上锁
lock.acquire()#死锁
#不执行
globalnum
foriinrange(10_000_000):
num-=1
lock.release()
lock.release()


if__name__=="__main__":
lock=threading.Lock()

subThread01=threading.Thread(target=add)
subThread02=threading.Thread(target=sub)

subThread01.start()
subThread02.start()

subThread01.join()
subThread02.join()

print("numresult:%s"%num)

with语句

由于threading.Lock()对象中实现了__enter__()与__exit__()方法,故我们可以使用with语句进行上下文管理形式的加锁解锁操作:

importthreading

num=0


defadd():
withlock:
#自动加锁
globalnum
foriinrange(10_000_000):
num+=1
#自动解锁


defsub():
withlock:
#自动加锁
globalnum
foriinrange(10_000_000):
num-=1
#自动解锁


if__name__=="__main__":
lock=threading.Lock()

subThread01=threading.Thread(target=add)
subThread02=threading.Thread(target=sub)

subThread01.start()
subThread02.start()

subThread01.join()
subThread02.join()

print("numresult:%s"%num)

#结果三次采集
#numresult:0
#numresult:0
#numresult:0

2、RLock() 递归锁

基本介绍

递归锁是同步锁的一个升级版本,在同步锁的基础上可以做到连续重复使用多次acquire()后再重复使用多次release()的操作,但是一定要注意加锁次数和解锁次数必须一致,否则也将引发死锁现象。

下面是threading模块与递归锁提供的相关方法:

方法 描述
threading.RLock() 返回一个递归锁对象
lockObject.acquire(blocking=True, timeout=1) 上锁,当一个线程在执行被上锁代码块时,将不允许切换到其他线程运行,默认锁失效时间为1秒
lockObject.release() 解锁,当一个线程在执行未被上锁代码块时,将允许系统根据策略自行切换到其他线程中运行
lockObject.locaked() 判断该锁对象是否处于上锁状态,返回一个布尔值

使用方式

以下是递归锁的简单使用,下面这段操作如果使用同步锁则会发生死锁现象,但是递归锁不会:

importthreading

num=0


defadd():
lock.acquire()
lock.acquire()
globalnum
foriinrange(10_000_000):
num+=1
lock.release()
lock.release()


defsub():
lock.acquire()
lock.acquire()
globalnum
foriinrange(10_000_000):
num-=1
lock.release()
lock.release()


if__name__=="__main__":
lock=threading.RLock()

subThread01=threading.Thread(target=add)
subThread02=threading.Thread(target=sub)

subThread01.start()
subThread02.start()

subThread01.join()
subThread02.join()

print("numresult:%s"%num)

#结果三次采集
#numresult:0
#numresult:0
#numresult:0

with语句

由于threading.RLock()对象中实现了__enter__()与__exit__()方法,故我们可以使用with语句进行上下文管理形式的加锁解锁操作:

importthreading

num=0


defadd():
withlock:
#自动加锁
globalnum
foriinrange(10_000_000):
num+=1
#自动解锁


defsub():
withlock:
#自动加锁
globalnum
foriinrange(10_000_000):
num-=1
#自动解锁


if__name__=="__main__":
lock=threading.RLock()

subThread01=threading.Thread(target=add)
subThread02=threading.Thread(target=sub)

subThread01.start()
subThread02.start()

subThread01.join()
subThread02.join()

print("numresult:%s"%num)

#结果三次采集
#numresult:0
#numresult:0
#numresult:0

3、Condition() 条件锁

基本介绍

条件锁是在递归锁的基础上增加了能够暂停线程运行的功能。并且我们可以使用wait()与notify()来控制线程执行的个数。

注意:条件锁可以自由设定一次放行几个线程。

下面是threading模块与条件锁提供的相关方法:

方法 描述
threading.Condition() 返回一个条件锁对象
lockObject.acquire(blocking=True, timeout=1) 上锁,当一个线程在执行被上锁代码块时,将不允许切换到其他线程运行,默认锁失效时间为1秒
lockObject.release() 解锁,当一个线程在执行未被上锁代码块时,将允许系统根据策略自行切换到其他线程中运行
lockObject.wait(timeout=None) 将当前线程设置为“等待”状态,只有该线程接到“通知”或者超时时间到期之后才会继续运行,在“等待”状态下的线程将允许系统根据策略自行切换到其他线程中运行
lockObject.wait_for(predicate, timeout=None) 将当前线程设置为“等待”状态,只有该线程的predicate返回一个True或者超时时间到期之后才会继续运行,在“等待”状态下的线程将允许系统根据策略自行切换到其他线程中运行。注意:predicate参数应当传入一个可调用对象,且返回结果为bool类型
lockObject.notify(n=1) 通知一个当前状态为“等待”的线程继续运行,也可以通过参数n通知多个
lockObject.notify_all() 通知所有当前状态为“等待”的线程继续运行

使用方式

下面这个案例会启动10个子线程,并且会立即将10个子线程设置为等待状态。

然后我们可以发送一个或者多个通知,来恢复被等待的子线程继续运行:

importthreading

currentRunThreadNumber=0
maxSubThreadNumber=10


deftask():
globalcurrentRunThreadNumber
thName=threading.currentThread().name

condLock.acquire()#上锁
print("startandwaitrunthread:%s"%thName)

condLock.wait()#暂停线程运行、等待唤醒
currentRunThreadNumber+=1
print("carryonrunthread:%s"%thName)

condLock.release()#解锁


if__name__=="__main__":
condLock=threading.Condition()

foriinrange(maxSubThreadNumber):
subThreadIns=threading.Thread(target=task)
subThreadIns.start()

whilecurrentRunThreadNumber< maxSubThreadNumber:
        notifyNumber = int(
            input("Please enter the number of threads that need to be notified to run:"))

condLock.acquire()
condLock.notify(notifyNumber)#放行
condLock.release()

print("mainthreadrunend")

#先启动10个子线程,然后这些子线程会全部变为等待状态
#startandwaitrunthread:Thread-1
#startandwaitrunthread:Thread-2
#startandwaitrunthread:Thread-3
#startandwaitrunthread:Thread-4
#startandwaitrunthread:Thread-5
#startandwaitrunthread:Thread-6
#startandwaitrunthread:Thread-7
#startandwaitrunthread:Thread-8
#startandwaitrunthread:Thread-9
#startandwaitrunthread:Thread-10

#批量发送通知,放行特定数量的子线程继续运行
# Please enter the number of threads that need to be notified to run:5 #放行5个
#carryonrunthread:Thread-4
#carryonrunthread:Thread-3
#carryonrunthread:Thread-1
#carryonrunthread:Thread-2
#carryonrunthread:Thread-5

# Please enter the number of threads that need to be notified to run:5 #放行5个
#carryonrunthread:Thread-8
#carryonrunthread:Thread-10
#carryonrunthread:Thread-6
#carryonrunthread:Thread-9
#carryonrunthread:Thread-7

# Please enter the number of threads that need to be notified to run:1
#mainthreadrunend

with语句

由于threading.Condition()对象中实现了__enter__()与__exit__()方法,故我们可以使用with语句进行上下文管理形式的加锁解锁操作:

importthreading

currentRunThreadNumber=0
maxSubThreadNumber=10


deftask():
globalcurrentRunThreadNumber
thName=threading.currentThread().name

withcondLock:
print("startandwaitrunthread:%s"%thName)
condLock.wait()#暂停线程运行、等待唤醒
currentRunThreadNumber+=1
print("carryonrunthread:%s"%thName)


if__name__=="__main__":
condLock=threading.Condition()

foriinrange(maxSubThreadNumber):
subThreadIns=threading.Thread(target=task)
subThreadIns.start()

whilecurrentRunThreadNumber< maxSubThreadNumber:
        notifyNumber = int(
            input("Please enter the number of threads that need to be notified to run:"))

withcondLock:
condLock.notify(notifyNumber)#放行

print("mainthreadrunend")

4、Event() 事件锁

基本介绍

事件锁是基于条件锁来做的,它与条件锁的区别在于一次只能放行全部,不能放行任意个数量的子线程继续运行。

我们可以将事件锁看为红绿灯,当红灯时所有子线程都暂停运行,并进入“等待”状态,当绿灯时所有子线程都恢复“运行”。

下面是threading模块与事件锁提供的相关方法:

方法 描述
threading.Event() 返回一个事件锁对象
lockObject.clear() 将事件锁设为红灯状态,即所有线程暂停运行
lockObject.is_set() 用来判断当前事件锁状态,红灯为False,绿灯为True
lockObject.set() 将事件锁设为绿灯状态,即所有线程恢复运行
lockObject.wait(timeout=None) 将当前线程设置为“等待”状态,只有该线程接到“绿灯通知”或者超时时间到期之后才会继续运行,在“等待”状态下的线程将允许系统根据策略自行切换到其他线程中运行

使用方式

事件锁不能利用with语句来进行使用,只能按照常规方式。

如下所示,我们来模拟线程和红绿灯的操作,红灯停,绿灯行:

importthreading

maxSubThreadNumber=3


deftask():
thName=threading.currentThread().name
print("startandwaitrunthread:%s"%thName)
eventLock.wait()#暂停运行,等待绿灯
print("greenlight,%scarryonrun"%thName)
print("redlight,%sstoprun"%thName)
eventLock.wait()#暂停运行,等待绿灯
print("greenlight,%scarryonrun"%thName)
print("subthread%srunend"%thName)


if__name__=="__main__":

eventLock=threading.Event()

foriinrange(maxSubThreadNumber):
subThreadIns=threading.Thread(target=task)
subThreadIns.start()

eventLock.set()#设置为绿灯
eventLock.clear()#设置为红灯
eventLock.set()#设置为绿灯

#startandwaitrunthread:Thread-1
#startandwaitrunthread:Thread-2
#startandwaitrunthread:Thread-3

#greenlight,Thread-1carryonrun
#redlight,Thread-1stoprun
#greenlight,Thread-1carryonrun
#subthreadThread-1runend

#greenlight,Thread-3carryonrun
#redlight,Thread-3stoprun
#greenlight,Thread-3carryonrun
#subthreadThread-3runend

#greenlight,Thread-2carryonrun
#redlight,Thread-2stoprun
#greenlight,Thread-2carryonrun
#subthreadThread-2runend

5、Semaphore() 信号量锁

基本介绍

信号量锁也是根据条件锁来做的,它与条件锁和事件锁的区别如下:

  • 条件锁:一次可以放行任意个处于“等待”状态的线程
  • 事件锁:一次可以放行全部的处于“等待”状态的线程
  • 信号量锁:通过规定,成批的放行特定个处于“上锁”状态的线程

下面是threading模块与信号量锁提供的相关方法:

方法 描述
threading.Semaphore() 返回一个信号量锁对象
lockObject.acquire(blocking=True, timeout=1) 上锁,当一个线程在执行被上锁代码块时,将不允许切换到其他线程运行,默认锁失效时间为1秒
lockObject.release() 解锁,当一个线程在执行未被上锁代码块时,将允许系统根据策略自行切换到其他线程中运行

使用方式

以下是使用示例,你可以将它当做一段限宽的路段,每次只能放行相同数量的线程:

importthreading
importtime

maxSubThreadNumber=6


deftask():
thName=threading.currentThread().name
semaLock.acquire()
print("runsubthread%s"%thName)
time.sleep(3)
semaLock.release()


if__name__=="__main__":
#每次只能放行2个
semaLock=threading.Semaphore(2)

foriinrange(maxSubThreadNumber):
subThreadIns=threading.Thread(target=task)
subThreadIns.start()


#runsubthreadThread-1
#runsubthreadThread-2

#runsubthreadThread-3
#runsubthreadThread-4

#runsubthreadThread-6
#runsubthreadThread-5

with语句

由于threading.Semaphore()对象中实现了__enter__()与__exit__()方法,故我们可以使用with语句进行上下文管理形式的加锁解锁操作:

importthreading
importtime

maxSubThreadNumber=6


deftask():
thName=threading.currentThread().name
withsemaLock:
print("runsubthread%s"%thName)
time.sleep(3)


if__name__=="__main__":

semaLock=threading.Semaphore(2)

foriinrange(maxSubThreadNumber):
subThreadIns=threading.Thread(target=task)
subThreadIns.start()

锁关系浅析

上面5种锁可以说都是基于同步锁来做的,这些你都可以从源码中找到答案。

首先来看RLock递归锁,递归锁的实现非常简单,它的内部会维护着一个计数器,当计数器不为0的时候该线程不能被I/O操作和时间轮询机制切换。但是当计数器为0的时候便不会如此了:

def__init__(self):
self._block=_allocate_lock()
self._owner=None
self._count=0#计数器

而Condition条件锁的内部其实是有两把锁的,一把底层锁(同步锁)一把高级锁(递归锁)。

低层锁的解锁方式有两种,使用wait()方法会暂时解开底层锁同时加上一把高级锁,只有当接收到别的线程里的notfiy()后才会解开高级锁和重新上锁低层锁,也就是说条件锁底层是根据同步锁和递归锁的不断切换来进行实现的:

def__init__(self,lock=None):
iflockisNone:
lock=RLock()#可以看到条件锁的内部是基于递归锁,而递归锁又是基于同步锁来做的
self._lock=lock

self.acquire=lock.acquire
self.release=lock.release
try:
self._release_save=lock._release_save
exceptAttributeError:
pass
try:
self._acquire_restore=lock._acquire_restore
exceptAttributeError:
pass
try:
self._is_owned=lock._is_owned
exceptAttributeError:
pass
self._waiters=_deque()

Event事件锁内部是基于条件锁来做的:

classEvent:

def__init__(self):
self._cond=Condition(Lock())#实例化出了一个条件锁。
self._flag=False

def_reset_internal_locks(self):
#private!calledbyThread._reset_internal_locksby_after_fork()
self._cond.__init__(Lock())

defis_set(self):
"""Returntrueifandonlyiftheinternalflagistrue."""
returnself._flag

isSet=is_set

Semaphore信号量锁内部也是基于条件锁来做的:

classSemaphore:

def__init__(self,value=1):
ifvalue< 0:
raiseValueError("semaphoreinitialvaluemustbe>=0")
self._cond=Condition(Lock())#可以看到,这里是实例化出了一个条件锁
self._value=value

基本练习题

条件锁的应用

需求:一个空列表,两个线程轮番往里面加值(一个加偶数,一个加奇数),最终让该列表中的值为 1 - 100 ,且是有序排列的。

importthreading

lst=[]


defeven():
"""加偶数"""
withcondLock:
foriinrange(2,101,2):
#判断当前列表的长度处于2是否能处尽
#如果能处尽则代表需要添加奇数
#否则就添加偶数
iflen(lst)%2!=0:
#添偶数
lst.append(i)#先添加值
condLock.notify()#告诉另一个线程,你可以加奇数了,但是这里不会立即交出执行权
condLock.wait()#交出执行权,并等待另一个线程通知加偶数
else:
#添奇数
condLock.wait()#交出执行权,等待另一个线程通知加偶数
lst.append(i)
condLock.notify()
condLock.notify()


defodd():
"""加奇数"""
withcondLock:
foriinrange(1,101,2):
iflen(lst)%2==0:
lst.append(i)
condLock.notify()
condLock.wait()
condLock.notify()


if__name__=="__main__":
condLock=threading.Condition()

addEvenTask=threading.Thread(target=even)
addOddTask=threading.Thread(target=odd)

addEvenTask.start()
addOddTask.start()

addEvenTask.join()
addOddTask.join()

print(lst)

事件锁的应用

有2个任务线程来扮演李白和杜甫,如何让他们一人一句进行对答?文本如下:

杜甫:老李啊,来喝酒!

李白:老杜啊,不喝了我喝不下了!

杜甫:老李啊,再来一壶?

杜甫:...老李?

李白:呼呼呼...睡着了..

代码如下:

importthreading


deflibai():
event.wait()
print("李白:老杜啊,不喝了我喝不下了!")
event.set()
event.clear()
event.wait()
print("李白:呼呼呼...睡着了..")

defdufu():
print("杜甫:老李啊,来喝酒!")
event.set()
event.clear()
event.wait()
print("杜甫:老李啊,再来一壶?")
print("杜甫:...老李?")
event.set()


if__name__=='__main__':

event=threading.Event()

t1=threading.Thread(target=libai)
t2=threading.Thread(target=dufu)

t1.start()
t2.start()
t1.join()
t2.join()

原文链接:https://www.cnblogs.com/YunyaSir/p/14963682.html

编辑:jq
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 源码
    +关注

    关注

    8

    文章

    648

    浏览量

    29306
  • python
    +关注

    关注

    56

    文章

    4801

    浏览量

    84850

原文标题:Python 中最常用的 5 种线程锁你会用吗?

文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Python常用函数大全

    Python 世界里,有一些宝藏函数和模块,它们可以让编程更轻松、代码更高效。这篇文章将带你一一认识这些神器,让的开发生活瞬间轻松不少!
    的头像 发表于 10-27 17:20 299次阅读

    Python中多线程和多进程的区别

    Python作为一种高级编程语言,提供了多种并发编程的方式,其中多线程与多进程是最常见的两种方式之一。在本文中,我们将探讨Python中多线程
    的头像 发表于 10-23 11:48 442次阅读
    <b class='flag-5'>Python</b>中多<b class='flag-5'>线程</b>和多进程的区别

    几种常用的共模电感磁芯类型

    电子发烧友网站提供《几种常用的共模电感磁芯类型.docx》资料免费下载
    发表于 09-20 11:27 0次下载

    常用的d存器型号有哪些

    D存器是一种常见的数字逻辑电路,用于存储一个二进制位的状态。以下是一些常用的D存器型号及其特点: 74LS74:这是一种低功耗的正触发D存器,具有4个独立的
    的头像 发表于 08-28 09:13 901次阅读

    一文掌握Python线程

    使用线程可以把占据长时间的程序中的任务放到后台去处理。
    的头像 发表于 08-05 15:46 891次阅读

    wds的常用组网有哪几种?

    (Point-to-Point)模式 点对点模式是WDS中最常用的一种组网方式,主要用于实现两个或多个无线设备之间的直接通信。在这种模式下,两个无线设备之间建立一个无线链路,实现数据的传输。这种模式适用于两个无线设备之间的距离较近,且没有其他无线设备的干扰。 1.1 单点对单点 单点对单点
    的头像 发表于 07-18 09:52 1179次阅读

    互斥和自旋的实现原理

    互斥和自旋是操作系统中常用的同步机制,用于控制对共享资源的访问,以避免多个线程或进程同时访问同一资源,从而引发数据不一致或竞争条件等问题。 互斥
    的头像 发表于 07-10 10:07 540次阅读

    自旋和互斥的使用场景是什么

    自旋和互斥是两种常见的同步机制,它们在多线程编程中被广泛使用。在本文中,我们将介绍自旋和互斥的使用场景,以及它们在不同场景下的优势和
    的头像 发表于 07-10 10:05 1058次阅读

    深度学习常用Python

    深度学习常用Python库,包括核心库、可视化工具、深度学习框架、自然语言处理库以及数据抓取库等,并详细分析它们的功能和优势。
    的头像 发表于 07-03 16:04 675次阅读

    功能测试覆盖中最常见的是什么方法

    功能测试覆盖是软件测试过程中的一个重要环节,它主要关注软件产品的功能实现是否符合需求规格说明。在功能测试覆盖中,有多种方法可以采用,以确保测试的全面性和有效性。本文将详细介绍功能测试覆盖中最常
    的头像 发表于 05-30 14:55 787次阅读

    PLC编程几种最常用的控制电路

    实现Y10的启动、保持和停止的四种梯形图如图所示。这些梯形图均能实现启动、保持和停止的功能。x0为启动信号,X1为停止信号。图a、c是利用Y10常开触点实现自保持,而图b、d是利用SET,RST指令实现自保持。
    发表于 03-27 11:19 1698次阅读
    PLC编程<b class='flag-5'>几种</b><b class='flag-5'>最常用</b>的控制电路

    几种常用单片机的在线升级能力比较

    电子发烧友网站提供《几种常用单片机的在线升级能力比较.pdf》资料免费下载
    发表于 03-24 09:39 2次下载

    java实现多线程几种方式

    Java实现多线程几种方式 多线程是指程序中包含了两个或以上的线程,每个线程都可以并行执行不同的任务或操作。Java中的多
    的头像 发表于 03-14 16:55 764次阅读

    python中5种线程盘点

    线程安全是多线程或多进程编程中的一个概念,在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确
    发表于 03-07 11:08 1641次阅读
    <b class='flag-5'>python</b>中5种<b class='flag-5'>线程</b><b class='flag-5'>锁</b>盘点

    计算机网络中最常用的通信协议

    绞线是目前最常用的传输介质之一,它由两根绞在一起的导线组成,可用于传输模拟或数字信号。同轴电缆也是一种常用的传输介质,它由内外两层导体组成,中间填充绝缘材料。
    发表于 02-19 09:51 801次阅读