Python中的多线程
#python #并发性 #multithreading

多线程

您好,您好!我将从基础知识开始,然后将您带到概念的深处。我将使用笔记样式的说明以及很多动手代码片段,您也可以在面试中修改。

了解多线程,让我们了解多任务。

多任务是同时执行多个任务的过程。

有两种类型的多任务:

  1. 基于过程的多任务

  2. 基于线程的多任务

  3. 基于过程的多任务

同时执行几个任务,每个任务是一个单独的独立过程,称为基于过程的多任务。

示例:

在Web编辑器中输入注释时,我们正在同时记录会话。

现在,同时我们也可以从Internet下载文件。同样,我们也可以播放一些音乐。所有这些任务都是同时彼此独立执行。因此,这是多任务的最佳示例。

这种类型的多任务处理适用于OS级别。

  1. 基于线程的多任务:

同时执行几个任务,每个任务是同一程序的独立部分,称为基于线程的多任务,其中每个独立零件称为线程。

这种类型的多任务适用于程序化级别。

基于线程的多任务的另一个名称是多线程。

注意:无论是基于过程还是基于线程的多任务处理,多任务的主要优点是通过减少响应时间来提高系统的性能。

多线程的一些主要重要应用领域是:

  1. 实现多媒体图形。

  2. 开发动画。

  3. 开发视频游戏。

  4. 开发网络和应用程序服务器。

etc

注意:在有一组独立作业的情况下,强烈建议同时执行而不是一个人执行。对于这种类型的情况,我们应该进行多线程。

Python提供了一个内置的模块“螺纹”,以提供为开发线程的支持。因此,Python线程模块使我们的任务更容易开发多线程。

默认情况下每个Python程序都包含一个称为主线程的线程。

示例:

#program to print the name of current executing thread
#importing the threading module
import threading

print("Current executing thread is: ", threading.current_thread().getName())

输出:

py .\\main-thread.py
Current executing thread is:  MainThread

螺纹模块包含一个函数current_thread(),该函数返回当前执行线程对象。在此对象上,如果我们调用getName()方法,那么我们将获得当前执行线程名称。

在Python中创建线程的不同方式:

主要在python中,我们可以以3种方式创建线程:

  1. 创建线程而不使用任何类

  2. 通过扩展线程类创建线程

  3. 创建线程而不扩展线程类。

  4. 在不使用任何类的情况下创建线程:

from threading import * 
def display():
    for i in range(1,11):
        print("Child Thread")

#creating a Thread object  explicitly
t = Thread(target=display)
#starting our thread by using start()
t.start()
for i in range(1,11):
    print("Main Thread")

输出:

py .\\thread-without-class.py
Child Thread
Child Thread
Child Thread
Main Thread 
Main Thread 
Child Thread
Main Thread
Child Thread
Main Thread
Child Thread
Main Thread
Child Thread
Main Thread
Main Thread
Main Thread
Main Thread
Child Thread
Main Thread
Child Thread
Child Thread

如上程序中所示,如果我们程序中存在多个线程,那么我们不能期望执行顺序,因此我们不能期望任何多线程程序的精确输出。

由于上述原因,我们无法为上述程序提供精确的输出。输出将因机器而异,并运行到运行。

  1. 通过扩展线程类创建线程:

用于通过扩展线程类创建线程,我们需要为线程类创建一个子类。

在那个子类中,我们需要使用所需的工作覆盖Run()方法。每当我们调用start()方法时,将执行自动运行()方法并将执行我们的作业。

from threading import *
#implementing the child class for the parent Thread class 
class MyThread(Thread):
    def run(self):
        for i in range(10):
            print("Child Thread-1")

t = MyThread()
t.start()
for i in range(10):
    print("Main Thread-1")

ouput:

py .\\extending-thread-class.py
Child Thread-1
Main Thread-1
Child Thread-1
Main Thread-1
Child Thread-1
Main Thread-1
Child Thread-1
Main Thread-1
Main Thread-1
Child Thread-1
Main Thread-1
Child Thread-1
Child Thread-1
Main Thread-1
Child Thread-1
Main Thread-1
Child Thread-1
Main Thread-1
Main Thread-1
  1. 创建线程而不扩展线程类:
#Creating a thread by using class but not extending the Thread class
from threading import * 
class WithoutThread:
    def display(self):
        for i in range(10):
            print("Child Thread")

wt = WithoutThread()
t = Thread(target=wt.display)
t.start()
for i in range(10):
    print("Main Thread!")

输出

py .\\thread-without-extending-thread-class.py
Child Thread
Main Thread!
Child Thread
Main Thread!
Child Thread
Main Thread!
Child Thread
Main Thread!
Main Thread!
Child Thread
Main Thread!
Main Thread!
Child Thread
Child Thread
Main Thread!
Child Thread
Main Thread!
Child Thread
Child Thread
Main Thread!

编写一个普通程序,而无需多线程以计算列表项目的正方形和双打

#write a program to find double of a number and square of a number
import time
def doubles(nums):
    for n in nums:
        time.sleep(1)
        print("Double of ",n," is :",(2*n))

def squares(nums):
    for n in nums:
        time.sleep(1)
        print("Square of ", n, " is: ",(n*n))

nums = [1,2,3,4,5]
beginTime = time.time()
doubles(nums)
squares(nums)
print("the total time taken: ", (time.time() - beginTime))

ouput:

py .\\without-multithreading.py
Double of  1  is : 2
Double of  2  is : 4
Double of  3  is : 6
Double of  4  is : 8
Double of  5  is : 10
Square of  1  is:  1
Square of  2  is:  4
Square of  3  is:  9
Square of  4  is:  16
Square of  5  is:  25
the total time taken:  10.078748226165771

同一程序如果我们使用多线程编写和运行,则

#write a program to find double of a number and square of a number
from threading import *
import time
def doubles(nums):
    for n in nums:
        time.sleep(1)
        print("Double of ",n," is :",(2*n))

def squares(nums):
    for n in nums:
        time.sleep(1)
        print("Square of ", n, " is: ",(n*n))

nums = [1,2,3,4,5]
beginTime = time.time()
#creating first thread
t1 = Thread(target=doubles,args=(nums,))
t2 = Thread(target=squares,args=(nums,))
t1.start()
t2.start()
t1.join()
t2.join()
print("the total time taken: ", (time.time() - beginTime))

ouput:

py .\\with-multithreading.py
Double of  1  is : 2
Square of  1  is:  1
Square of  2  is:  4
Double of  2  is : 4
Double of  3  is : 6
Square of  3  is:  9
Double of  4  is : 8
Square of  4  is:  16
Double of  5  is : 10
Square of  5  is:  25
the total time taken:  5.035094738006592

设置并获取线程的名称:

Python中的每个线程都有一个名称。它可能是由Python解释器或我们提供的自定义名称生成的默认名称(程序员)。

现在,我们可以使用线程类的特殊方法来获取或设置线程的名称IE

  1. t.getName()â>返回线程名称

  2. t.setname(newname)â>设置我们自己的线程名称。

thead标识号(eside):

对于每个内部线程,都有一个唯一的标识号。我们可以使用隐式变量“ IDENS”访问这些ID。

from threading import * 
print("current thread is: ", current_thread().getName())
current_thread().setName("First Thread")
print("current thread is: ", current_thread().getName())
print(current_thread().name)
#identification Number is a Thread 
print(current_thread().ident)
py .\\threadName.py
current thread is:  MainThread
current thread is:  First Thread
First Thread
16404

示例-2

#identifying the threads with its identity number
from threading import * 
def identity():
    print("Child thread")

t = Thread(target=identity)
t.start()

print("Main thread identification number :", current_thread().ident)
print("Child thread identification number: ", t.ident)

输出

py .\\thread-identity.py
Child thread
Main thread identification number : 9548
Child thread identification number:  12348

active_count():

要计算当前正在运行的活动线程的数量,我们可以使用线程模块的active_count()函数

示例:

#program to count the number of active threads using active_count() of threading module
from threading import * 
import time
def display():
    print(current_thread().getName(),"...started")
    time.sleep(3)
    print(current_thread().getName(),"...ended")
print("The number of active threads: ", active_count())
t1 = Thread(target=display,name="ChildThread-1")
t2 = Thread(target=display,name="ChildThread-2")
t3 = Thread(target=display,name="ChildThread-3")
t1.start()
t2.start()
t3.start()
print("The number of active threads are:  ",active_count())
time.sleep(5)
print("The number of active threads are: ",active_count())

输出:

py .\\active-thread.py
The number of active threads:  1
ChildThread-1 ...started
ChildThread-2 ...started
ChildThread-3 ...started
The number of active threads are:   4
ChildThread-2 ...ended
ChildThread-3 ...ended
ChildThread-1 ...ended
The number of active threads are:  1

枚举()函数:

此功能返回当前运行的所有活动线程的列表

#using enumerate() of threading module
from threading import *
import time
#creating a funtion
def display():
    print(current_thread().getName(),">>..started")
    time.sleep(3)
    print(current_thread().getName(),">>..ended")

#creating a thread 
t1 = Thread(target=display,name="ChildThread1")
t2 = Thread(target=display,name="Childthread2")
t3 = Thread(target=display,name="ChildThread3")
t1.start()
t2.start()
t3.start()
thread_list = enumerate()
for thread in thread_list:
    print("Thread Name:",thread.name)
time.sleep(5)
thread_list = enumerate()
for thread in thread_list:
    print("Thread name is: ",thread.name)
py .\\thread-list.py
ChildThread1 >>..started
Childthread2 >>..started 
ChildThread3 >>..started 
Thread Name: MainThread  
Thread Name: ChildThread1
Thread Name: Childthread2
Thread Name: ChildThread3
ChildThread3 >>..ended
Childthread2 >>..ended
ChildThread1 >>..ended
Thread name is:  MainThread

isalive():

此方法用于检查线程是否仍在执行。

示例:

#Checking whether a thread is alive or not 
from threading import * 
import time
def display():
    print(current_thread().getName(),">>...started")
    time.sleep(3)
    print(current_thread().getName(),">>..ended")

t1 = Thread(target=display,name="childThread1")
t2 = Thread(target=display,name="childThread2")
t1.start()
t2.start()
print(t1.name,"is Alive",t1.is_alive())
print(t2.name,"is Alive",t2.is_alive())
time.sleep(5)
print(t1.name,"is Alive",t1.is_alive())
print(t2.name,"is Alive",t2.is_alive())
#task check whether the main thread is alive or not if alive when it dies

ouput

py .\\isAliveThread.py
childThread1 >>...started
childThread2 >>...started
childThread1 is Alive True
childThread2 is Alive True
childThread1 >>..ended
childThread2 >>..ended
childThread1 is Alive False
childThread2 is Alive False

join()方法:

此方法用于等到完成其他线程。即

当前运行其他线程后,将执行将调用Join()方法的线程。

#executing a thread after some thread process using join() method
from threading import * 
import time
def display():
    for i in range(10):
        print("Snehal's Thread")
        time.sleep(2)

t = Thread(target=display)
t.start()
#The blow line of code will be executed by Main Thread
t.join()
for i in range(10):
    print("Main Thread")

输出:

py .\\joinThread.py
Snehal's Thread
Snehal's Thread
Snehal's Thread
Snehal's Thread
Snehal's Thread
Snehal's Thread
Snehal's Thread
Snehal's Thread
Snehal's Thread
Snehal's Thread
Main Thread
Main Thread
Main Thread
Main Thread
Main Thread
Main Thread
Main Thread
Main Thread
Main Thread
Main Thread

注意:我们可以在时间段以及下面显示的
中调用Join()方法

t.join(seconds)
where t--> object reference on which we are calling the join(seconds) method
            seconds--> time in seconds till which this thread will wait

示例:

#executing a thread after some thread process using join() method
from threading import * 
import time
def display():
    for i in range(10):
        print("Snehal's Thread")
        time.sleep(2)

t = Thread(target=display)
t.start()
#The blow line of code will be executed by Main Thread
t.join(10)
for i in range(10):
    print("Main Thread")

输出:

py .\\joinThread.py
Snehal's Thread
Snehal's Thread
Snehal's Thread
Snehal's Thread
Snehal's Thread
Main Thread
Main Thread
Main Thread
Main Thread
Main Thread
Main Thread
Main Thread
Main Thread
Main Thread
Main Thread
Snehal's Thread
Snehal's Thread
Snehal's Thread
Snehal's Thread
Snehal's Thread

在上面的示例中,我们可以看到我们的主线程等待了10秒钟,按照Join(10)方法开始执行主线程。

守护程序线程:

在后台运行的线程称为守护程序。

守护程序线程的主要目标是为非daemon线程(如主线程)提供支持

例如:垃圾收集器

每当主线程以低内存而运行时,PVM会立即运行垃圾收集器以破坏无用的对象并提供免费内存,因此主线程可以继续执行而无需任何内存问题。

如何检查给定线程是否是守护程序线程?

我们可以通过两种方式检查守护程序线程:

  1. 使用线程类的isdaemon()方法

  2. 使用守护程序属性

from threading import *
print(current_thread().isDaemon())
print(current_thread().daemon)

输出:

py .\\daemon.py
False
False

我们也可以使用 setDaemon()线程类别的方法来更改守护程序。

但是,wee可以在启动线程IE之前使用此方法,即一旦线程启动,我们就无法更改其守护程序的性质,否则我们将获得如下所示:

from threading import *
print(current_thread().isDaemon())
print(current_thread().daemon)
print(current_thread().setDaemon(True))

ouput:

py .\\daemon.py
False
False
Traceback (most recent call last):
  File "D:\\Software Training Classes\\Pythonier\\class-58\\daemon.py", line 4, in <module>
    print(current_thread().setDaemon(True))
  File "C:\\Users\\PRAVEEN\\AppData\\Local\\Programs\\Python\\Python39\\lib\\threading.py", line 1154, in setDaemon
    self.daemon = daemonic
  File "C:\\Users\\PRAVEEN\\AppData\\Local\\Programs\\Python\\Python39\\lib\\threading.py", line 1147, in daemon
    raise RuntimeError("cannot set daemon status of active thread")
RuntimeError: cannot set daemon status of active thread

线程的默认性质是什么?

默认情况下,主线程始终是非daemon。

,但对于剩余的线程,守护程序的性质将从父母到孩子继承,即

如果父螺纹是守护程序,子线程也将是守护程序,反之亦然。

示例:

#default nature of a thread
from threading import *
def job():
    print("Child Thread")
t = Thread(target=job)
print("Daemon thread: ",t.isDaemon())
t.setDaemon(True)
print("Daemon Thread: ",t.isDaemon())

输出

py .\\default-daemon.py
Daemon thread:  False
Daemon Thread:  True

注意:主线程始终是非daemon的,我们不能更改其守护程序的性质,因为它仅在开始时就开始了。

每当最后一个非daemon线程自动终止时,所有守护程序线程都将被终止。

同步

如果多个线程同时执行,则可能存在以下程序所示的数据不一致问题:

from threading import *
import time
def wish(name):
    for i in range(10):
        print("Good morning",end='')
        time.sleep(2)
        print(name)
t1 = Thread(target=wish,args=('Snehal',))
t2 = Thread(target=wish,args=('apurva',))
t1.start()
t2.start()

输出:

py .\\synchronization.py
Good morningGood morningSnehal
apurva
Good morningGood morningapurva
Snehal
Good morningGood morningapurva
Snehal
Good morningGood morningapurva
Snehal
Good morningGood morningSnehal
apurva
Good morningGood morningapurva
Snehal
Good morningGood morningSnehal
apurva
Good morningGood morningSnehal
apurva
Good morningGood morningSnehal
apurva
Good morningGood morningSnehal
apurva

在上述程序中,我们将获得不规则的输出,因为螺纹T1和T2都同时执行WISH()函数。现在解决上述问题,我们使用同步。

同步是一个一个过程,在该过程中,线程被一个一个执行,以便我们可以克服数据不一致问题。

换句话说,同步仅表示一个线程

同步的一些主要应用领域是:

  1. 在线预订系统。

  2. 资金从联合帐户转移。

在Python中,我们可以使用

实现同步
  1. rlock

  2. 信号量

使用锁定概念

同步

锁定概念是Python中线程模块提供的最基本的同步机制。

我们可以如下创建一个锁定对象

lock1 = Lock()

锁定对象一次只能容纳一个线程。如果同时需要执行任何其他线程,则必须等到线程发布锁(这与洗手间,电话亭等非常相似)

线程如何获得锁?

线程可以使用Acceaire()方法来获取锁定。即

lock1.acquire()

获得锁的线程如何释放其锁?

线程可以使用Release()方法释放其锁定。

lock1.release()

注意:要注意的是,请致电Release()方法强制性线程应该是该锁的所有者。 IE线程应该已经锁定了,否则我们将获得RuntimeError:释放解锁锁

#to release a lock the thread should be already locked 
from threading import * 
lock1 = Lock()
#releasing  a thread from the lock 
lock1.release()

输出:

py .\\Lock-release.py
Traceback (most recent call last):
  File "D:\\Software Training Classes\\Pythonier\\class-59\\Lock-release.py", line 5, in <module>
    lock1.release()
RuntimeError: release unlocked lock

现在,为了避免上述错误,我们需要首先锁定锁()线程,如下所示

#to release a lock the thread should be already locked 
from threading import * 
import time
lock1 = Lock()
#locking a thread 
lock1.acquire()
print("The Thread is locked")
time.sleep(2)
#releasing  a thread from the lock 
lock1.release()
print("The thread is released from the lock!!")

现在我们不会像以前那样遇到任何错误

输出

py .\\Lock-release.py
The Thread is locked
The thread is released from the lock!!

示例1:使用锁定概念同步

#synchronization using LOCK concept
from threading import *
import time
lock1 = Lock()
def wish(name):
    lock1.acquire()
    for i in range(10):
        print("Good MOrning:",end='')
        time.sleep(2)
        print(name)
    lock1.release()

t1 = Thread(target=wish,args=("Snehal",))
t2 = Thread(target=wish,args=("Python",))
t3 = Thread(target=wish,args=("Apurva",))
t1.start()
t2.start()
t3.start()

ouput:

py .\\synchronization-lock.py
Good MOrning:Snehal
Good MOrning:Snehal
Good MOrning:Snehal
Good MOrning:Snehal
Good MOrning:Snehal
Good MOrning:Snehal
Good MOrning:Snehal
Good MOrning:Snehal
Good MOrning:Snehal
Good MOrning:Snehal
Good MOrning:Python
Good MOrning:Python
Good MOrning:Python
Good MOrning:Python
Good MOrning:Python
Good MOrning:Python
Good MOrning:Python
Good MOrning:Python
Good MOrning:Python
Good MOrning:Python
Good MOrning:Apurva
Good MOrning:Apurva
Good MOrning:Apurva
Good MOrning:Apurva
Good MOrning:Apurva
Good MOrning:Apurva
Good MOrning:Apurva
Good MOrning:Apurva
Good MOrning:Apurva
Good MOrning:Apurva

一次在上面的程序中,只允许一个线程执行wish()函数,因此我们将获得常规输出而不会混淆。

简单锁的问题:

标准锁定对象不在乎,哪个线程当前持有该锁定。如果锁定锁定,并且任何其他线程都试图获取该锁,则将被阻止,即使是同一线程已经持有锁。

#problem of simple thread Lock
from threading import *
lock1 = Lock()
print("Main Thread trying to acquire a Lock")
lock1.acquire()
print("Main Thread trying to acquire a Lock again..")
lock1.acquire()

输出:

py .\\simple-thread-block.py
Main Thread trying to acquire a Lock
Main Thread trying to acquire a Lock again..
execution blocked

在上面的程序中,主线程将被阻止,因为它试图第二次获取锁。

注意:要杀死Window命令提示符的阻止线程,我们必须使用Ctrl+Break。

如果线程调用递归功能或嵌套对资源的访问,则该线程可能会尝试一次又一次地获取相同的锁,这可能会阻止我们的线程。

因此,传统的锁定机制对于执行递归功能不起作用。为了克服这个问题,我们应该去rlock(重入锁)。

重新输入意味着线程可以一次又一次地获取相同的锁。如果锁定由其他线程持有,则仅将螺纹阻止。重点设施仅适用于所有者线程,但不适合其他线程。

from threading import * 
rlock = RLock()
print("Main Thread is acquire Lock")
rlock.acquire()
print("Main Thread trying to acquire the Lock again.")
rlock.acquire()

在上述情况下,主线程不会被锁定,因为线程可以多次获取锁。此rlock可以跟踪递归水平,因此应提供每个获取的呼叫强制性释放()呼叫。即,应匹配Acceaire()呼叫和repartion()调用的数量,然后仅发布锁。

2个释放()调用后,只会释放锁。注意:

  1. 只有所有者线程可以多次获取锁

  2. 应匹配Acceaire()呼叫的数量()调用()调用。

示例:使用rlock
同步

from threading import * 
import time
rlock = RLock()

#creating a factorial function
def factorial(n):
    rlock.acquire()
    if n == 0:
        result = 1
    else:
        result = n*factorial(n-1)
    rlock.release()
    return result

def results(n):
    print("the factorial of",n, "is ",factorial(n))

t1 = Thread(target=results,args=(5,))
t2 = Thread(target=results,args=(9,))
t1.start()
t2.start()

输出:

py .\\synchronization-rlock.py
the factorial of 5 is  120
the factorial of 9 is  362880

锁和rlock之间的区别

一次只能通过一个线程来获取锁定的llock锁定对象。甚至所有者线程也无法多次获取。 Rlock对象一次只能通过一个线程获取,但是所有者线程可以多次获取相同的锁定对象,不适合执行递归函数,并且嵌套的访问呼叫最适合执行递归功能,并且在这种情况下,锁定对象将采用lock lock对象。护理只锁定或解锁,它永远不会关心所有者线程和递归水平。在这种情况下

使用信号量

同步

在锁定和rlock的情况下,一次只允许一个线程执行。有时,我们的要求是一次允许特定线程访问的特定线程(例如,允许10个成员访问数据库服务器,允许4个成员访问网络连接等)。

>

要处理此要求,我们不能使用锁定和RLOCK概念,而应该使用信号量概念。信号量可用于限制容量有限的共享资源的访问。信号量是高级同步机制。我们可以创建一个信号对象,如下所示

Syntax:
sema = Semaphor(counter)

在上面的语法计数器中,表示允许最大螺纹数量同时访问。计数器的默认值为1。每当线程执行caceire()方法时,计数器值将被1降低,如果thread executes release()方法,则计数器值将被1增量1。计数器值将被降低,并且对于每个版本()呼叫计数器值将被递增。

case-1:s = Semaphore()在这种情况下,反值为1,一次只允许一个线程访问。它与锁定概念完全相同。

case-2:s =信号量(3)在这种情况下,可以通过3个线程访问信号量对象。其余线程必须等待直到释放信号量为止。

示例:使用Smahaphor
实现同步

from threading import * 
import time
sema = Semaphore(2)
def wish(name):
    sema.acquire()
    for i in range(10):
        print("Good Evening: ",end= ' ')
        time.sleep(2)
        print(name)

    sema.release()

t1 = Thread(target=wish,args=("Snehal",))
t2 = Thread(target=wish,args=("Apuva",))
t3 = Thread(target=wish,args=("Python",))
t4 = Thread(target=wish,args=("Postgresql",))
t5 = Thread(target=wish,args=("Django",))
t1.start()
t2.start()
t3.start()
t4.start()
t5.start()

输出:

py .\\sema-synchronous.py
Good Evening:  Good Evening:  Snehal
Apuva
Good Evening:  Good Evening:  Apuva
Snehal
Good Evening:  Good Evening:  Snehal
Apuva
Good Evening:  Good Evening:  Apuva
Snehal
Good Evening:  Good Evening:  Apuva
Snehal
Good Evening:  Good Evening:  Snehal
Apuva
Good Evening:  Good Evening:  Snehal
Apuva
Good Evening:  Good Evening:  Apuva
Snehal
Good Evening:  Good Evening:  Apuva
Snehal
Good Evening:  Good Evening:  Snehal
Apuva
Good Evening:  Good Evening:  Postgresql
Python
Good Evening:  Good Evening:  Postgresql
Python
Good Evening:  Good Evening:  Postgresql
Python
Good Evening:  Good Evening:  Postgresql
Python
Good Evening:  Good Evening:  Python
Postgresql
Good Evening:  Good Evening:  Python
Postgresql
Good Evening:  Good Evening:  Python
Postgresql
Good Evening:  Good Evening:  Python
Postgresql
Good Evening:  Good Evening:  Python
Postgresql
Good Evening:  Good Evening:  Python
Postgresql
Good Evening:  Django
Good Evening:  Django
Good Evening:  Django
Good Evening:  Django
Good Evening:  Django
Good Evening:  Django
Good Evening:  Django
Good Evening:  Django
Good Evening:  Django
Good Evening:  Django

在上面的程序中,允许2个线程访问信号量,因此允许2个线程同时执行wish()函数。

注意:通常,信号量是一个无限的信号量,它允许我们调用Release()方法多次增加计数器。发行()调用的次数也可以超过acealece()呼叫的数量。即

from threading import * 
sema = Semaphore(2)
sema.acquire()
sema.acquire()
sema.release()
sema.release()
sema.release()
sema.release()
print("Ended")

上面的程序没有任何错误。

有限的距离:

正常信号量是一个无限的信号量,它允许我们多次调用realease()方法来增加计数器。与Acceaire()调用的数量相比,超过了释放()调用的数量。

这种错误的混合被边界的emaphore清除。

边界的emaphore与信号量完全相同,只是release()方法调用的数量不应超过aceceire()方法调用的数量,否则我们将遇到错误。

示例:

#demo of boundedsemaphore
from threading import * 
bs = BoundedSemaphore(2)
bs.acquire()
bs.acquire()
bs.release()
bs.release()
bs.release()
print("End")

输出

py .\\boundedsemaphore.py
Traceback (most recent call last):
  File "D:\\Software Training Classes\\Pythonier\\class-61\\boundedsemaphore.py", line 8, in <module>      
    bs.release()
  File "C:\\Users\\PRAVEEN\\AppData\\Local\\Programs\\Python\\Python39\\lib\\threading.py", line 504, in release
    raise ValueError("Semaphore released too many times")
ValueError: Semaphore released too many times

上面的程序无效,因为Release()方法调用的数量应等于boundedsemaphore中的Acceaire()方法调用数量。

注意:为了防止简单的编程错误,建议在正常信号范围内使用有限的emaphore。

锁和信号量之间的区别

一个时间锁可以仅通过一个线程获取,但是可以通过计数器值指定的固定线程来获取信号量对象。

结论:

同步的主要优点是我们可以克服数据不一致问题。但是同步的主要缺点是增加了线程的等待时间并引起了性能问题。因此,如果没有特定要求,则不建议使用同步。

线程交流:

需要线程进行交流的概念称为Inter线程通信。

示例:生产项目后,生产者线程必须与消费者线程通知新项目。然后,只有消费者线程才能消耗新项目。

在Python语言中,我们可以使用以下不同的方式来实现Inter线程通信:

  1. 事件

  2. 条件

  3. 队列

etc

使用事件对象:

事件对象是线程之间最简单的通信机制。

在此线程中,线程向事件发出信号,其他线程等待它。

我们可以创建事件对象,如下所示:

event = threading.Event()

事件对象管理一个可以设置()或clear()的内部标志。

其他线程可以等到事件设置为止。

事件类的方法:

事件类别有四种方法:

  1. set():内部标志值将变为真,它代表所有等待线程的绿色信号。

  2. clear():内部标志值将变为false,代表所有等待线程的红色信号。

  3. isset():可以使用此方法检查是否设置了事件。

  4. wait()|等待(秒):线程可以等待设置。

事件的语法:

event = threading.Event()
#to tell the consumer thread to wait untill event is set 
event.wait()

#producer thread can set or clear the event 
event.set()
event.clear()

示例:

from threading import *
import time

def producer():
    time.sleep(5)
    print("Producer thread producing items:")
    print("Producer thread giving notification by setting the event.")
    event.set()

def consumer():
    print("Consumer thread is waiting for the updation:")
    event.wait()
    print("Consmer thread got notificaition and consuming the items.")

event = Event()
t1 = Thread(target=producer)
t2 = Thread(target=consumer)
t1.start()
t2.start()

输出:

py .\\interThread-comm.py
Consumer thread is waiting for the updation:
Producer thread producing items:
Producer thread giving notification by setting the event.
Consmer thread got notificaition and consuming the items.

示例2

#Inter Thread Communication example
from threading import * 
import time
def trafficpolice():
    while True:
        time.sleep(10)
        print("Traffic Police Giving GREEN signal.")
        event.set()
        time.sleep(20)
        print("Traffic Police giving red signal!!")
        event.clear()

def driver():
    num = 0
    while True:
        print("Driver waiting for the green signal:")
        event.wait()
        print("Traffic Signal turned Green.. Vechiles can start moving>>>")
        while event.isSet():
            num = num + 1
            print("Vechile no",num,"Crossing the signal")
            time.sleep(2)
            print("Traffic Signal turned RED.. Drivers have to wait again..")

event = Event()
t1 = Thread(target=trafficpolice)
t2 = Thread(target=driver)
t1.start()
t2.start()

输出:

py .\\trafffic-system-comm.py
Driver waiting for the green signal:
Traffic Police Giving GREEN signal.
Traffic Signal turned Green.. Vechiles can start moving>>>
Vechile no 1 Crossing the signal
Traffic Signal turned RED.. Drivers have to wait again..
Vechile no 2 Crossing the signal
Traffic Signal turned RED.. Drivers have to wait again..
Vechile no 3 Crossing the signal
Traffic Signal turned RED.. Drivers have to wait again..
Vechile no 4 Crossing the signal
Traffic Signal turned RED.. Drivers have to wait again..
Vechile no 5 Crossing the signal
Traffic Signal turned RED.. Drivers have to wait again..
Vechile no 6 Crossing the signal
Traffic Signal turned RED.. Drivers have to wait again..
Vechile no 7 Crossing the signal
Traffic Signal turned RED.. Drivers have to wait again..
Vechile no 8 Crossing the signal
Traffic Signal turned RED.. Drivers have to wait again..
Vechile no 9 Crossing the signal
Traffic Signal turned RED.. Drivers have to wait again..
Vechile no 10 Crossing the signal
Traffic Police giving red signal!!
Traffic Signal turned RED.. Drivers have to wait again..
Driver waiting for the green signal:
Traffic Police Giving GREEN signal.
Traffic Signal turned Green.. Vechiles can start moving>>>
Vechile no 11 Crossing the signal
Traffic Signal turned RED.. Drivers have to wait again..
Vechile no 12 Crossing the signal
Traffic Signal turned RED.. Drivers have to wait again..
Vechile no 13 Crossing the signal
Traffic Signal turned RED.. Drivers have to wait again..
Vechile no 14 Crossing the signal
Traffic Signal turned RED.. Drivers have to wait again..
Vechile no 15 Crossing the signal
Traffic Signal turned RED.. Drivers have to wait again..
Vechile no 16 Crossing the signal
Traffic Signal turned RED.. Drivers have to wait again..
Vechile no 17 Crossing the signal
Traffic Signal turned RED.. Drivers have to wait again..
Vechile no 18 Crossing the signal
Traffic Signal turned RED.. Drivers have to wait again..
Vechile no 19 Crossing the signal
Traffic Signal turned RED.. Drivers have to wait again..
Vechile no 20 Crossing the signal
Traffic Police giving red signal!!
Traffic Signal turned RED.. Drivers have to wait again..
Driver waiting for the green signal:
Traffic Police Giving GREEN signal.
Traffic Signal turned Green.. Vechiles can start moving>>>
Vechile no 21 Crossing the signal
Traffic Signal turned RED.. Drivers have to wait again..
Vechile no 22 Crossing the signal
Traffic Signal turned RED.. Drivers have to wait again..
Vechile no 23 Crossing the signal
Traffic Signal turned RED.. Drivers have to wait again..
Vechile no 24 Crossing the signal

在上面的程序中,驱动程序线程必须等到交通警察设置一个事件,即发出绿色信号。一旦交通信号线程设置事件(给出绿色信号),车辆就可以越过信号。

交通警察线程清除事件(给出红色信号)后,驾驶员线程必须再次等待。

使用条件对象的线程交流:

我们可以说条件是用于线际间通信的事件对象的更高级版本。条件代表应用程序中某种状态变化,例如生产项目或食用项目。

线程可以等待该条件,并且一旦发生条件,就可以通知线程。 IE条件对象允许一个或多个线程等到另一个线程通知。

条件始终与锁相关(重新进入)。

条件具有获取()和释放()方法,该方法调用相关锁的相应方法。

我们可以创建条件对象如下:

condition = threading.Condition()

条件对象的方法

下面给出了一些重要的条件对象方法:

  1. aceceire():在产生或消费items之前获取条件对象。 IE线程获取内部锁。

  2. 版本():生产或食用项目后释放条件对象。 IE线程释放内部锁。

  3. wait()|等待(时间):等到通知或时间过期。

  4. notify():给一个等待线程的通知。

  5. notifyall():给所有等待线程的通知。

案例研究

生产线程需要在生产资源并通知消费者之前获得条件。

#Producer Thread

...generate an item..

condition.acquire()

..add items to the resource..

condition.notify() # signal's that a new item is available(notifyAll())

condition.release()

消费者必须获得条件,然后可以从资源中食用项目

#Consumer Thread

condition.acquire()

condition.wait()

consume item

condition.release()

示例:

#demo of condtional object
from threading import *
def consume(c):
    c.acquire()
    print("Consumer waiting for the updation...")
    c.wait()
    print("Consumer got notification and consuming the item..")
    c.release()

#producer
def producer(c):
    c.acquire()
    print("Producer producing items")
    print("Producer giving notification.")
    c.notify()
    c.release()

#creating an condition object
condition_obj = Condition()
t1 = Thread(target=consume,args=(condition_obj,))
t2 = Thread(target=producer,args=(condition_obj,))
t1.start()
t2.start()

输出

py .\\condition-thread.py
Consumer waiting for the updation...
Producer producing items
Producer giving notification.
Consumer got notification and consuming the item..

示例2

#condition thread object
from threading import *
import time
import random
#creating list of items
items = []
def produce(c):
    while True:
        c.acquire()
        item = random.randint(1,100)
        print("Producer producing item: ",item)
        items.append(item)
        print("Producer giving notification")
        c.notify()
        c.release() # it turns the condition to false 
        time.sleep(5)

def consume(c):
    while True:
        c.acquire()
        print("Consumer waiting for the updation>>>")
        c.wait()
        print("Consumer got notification and consumed the item",items.pop())
        c.release()
        time.sleep(5)

# creating a condition object
condition_obj = Condition()
t1 = Thread(target=consume,args=(condition_obj,))
t2 = Thread(target=produce,args=(condition_obj,))
t1.start()
t2.start()

输出

py .\\condition-thread2.py
Consumer waiting for the updation>>>
Producer producing item:  61
Producer giving notification
Consumer got notification and consumed the item 61
Producer producing item:  32
Producer giving notification
Consumer waiting for the updation>>>
Producer producing item:  18
Producer giving notification
Consumer got notification and consumed the item 18
Producer producing item:  41
Producer giving notification
Consumer waiting for the updation>>>
Producer producing item:  62
Producer giving notification
Consumer got notification and consumed the item 62
Producer producing item:  64
Producer giving notification
Consumer waiting for the updation>>>
Producer producing item:  95
Producer giving notification
Consumer got notification and consumed the item 95
Producer producing item:  88
Producer giving notification
Consumer waiting for the updation>>>
Producer producing item:  19
Producer giving notification
Consumer got notification and consumed the item 19
Producer producing item:  16
Producer giving notification
Consumer waiting for the updation>>>
Producer producing item:  24
Producer giving notification
Consumer got notification and consumed the item 24
Producer producing item:  66
Producer giving notification
Consumer waiting for the updation>>>
Producer producing item:  96
Producer giving notification
Consumer got notification and consumed the item 96
Producer producing item:  25
Producer giving notification
Consumer waiting for the updation>>>
Producer producing item:  53
Producer giving notification
Consumer got notification and consumed the item 53
Producer producing item:  46
Producer giving notification
Consumer waiting for the updation>>>
Producer producing item:  1
Producer giving notification
Consumer got notification and consumed the item 1
Producer producing item:  76
Producer giving notification
Consumer waiting for the updation>>>

在上面的程序消费者线程中期望更新,因此负责在条件对象上调用wait()方法。

生产者线程执行更新,因此负责调用notify()或notifyall(()方法。

通过使用队列:队列概念是线程间通信和在线程之间共享数据的最增强的机制。

队列内部有条件,该状况有锁定。因此,每当我们使用队列时,我们都不必担心同步。

如果我们想使用队列拳头,我们应该通过导入来使用队列模块IE

import queue

之后,我们需要创建队列对象

ie

q = queue.Queue()

队列的重要方法:

  1. put():用于将项目放入队列中。

  2. get():它用于从队列中删除并返回项目。

生产者线程使用put()方法将数据插入队列。在内部,此方法具有逻辑,可以在将数据插入队列之前获取锁定。插入数据后,将自动释放锁。

there put()方法还检查队列是否已满,如果队列已满,那么生产者线程将通过内部调用Wait()方法输入等待状态。

cosumer thread使用get()方法来删除并从队列中获取数据。在内部,此方法具有逻辑以获取锁定,然后再从队列中删除数据。拆卸完成后,将自动释放锁。

如果队列为空,则消费者线程将通过内部调用Wait()方法输入等待状态。一旦队列更新了数据,则将自动通知线程。

注意:队列模块为我们锁定,这是很大的优势。

示例:

#making use of queue object
from threading import * 
import time
import random
import queue

#defining a producer
def produce(q):
    while True:
        item = random.randint(1,100)
        print("Producer producing item:", item)
        q.put(item)
        print("Producer giving notification")
        time.sleep(5)

#defining a consumer
def consume(q):
    while True:
        print("Consumer waiting for updation:")
        print("Consumer consumed an item:",q.get())
        time.sleep(5)

#creating a queue object
qu_obj = queue.Queue()
t1 = Thread(target=consume,args=(qu_obj,))
t2 = Thread(target=produce,args=(qu_obj,))
t1.start()
t2.start()

输出

py .\\queue-object.py
Consumer waiting for updation:
Producer producing item: 60
Producer giving notification
Consumer consumed an item: 60
Consumer waiting for updation:
Producer producing item: 49
Producer giving notification
Consumer consumed an item: 49
Consumer waiting for updation:
Producer producing item: 84
Producer giving notification
Consumer consumed an item: 84
Producer producing item: 49
Consumer waiting for updation:
Producer giving notification
Consumer consumed an item: 49
Producer producing item: 7
Producer giving notification
Consumer waiting for updation:
Consumer consumed an item: 7
Producer producing item: 66
Consumer waiting for updation:
Producer giving notification
Consumer consumed an item: 66
Producer producing item: 12
Consumer waiting for updation:
Producer giving notification
Consumer consumed an item: 12
Producer producing item: 58
Consumer waiting for updation:
Producer giving notification
Consumer consumed an item: 58
Producer producing item: 1

队列类型:

python中有三种类型的队列:

  1. fifo队列| [首先排队队列]:

fifo队列是默认队列。在这种类型的队列中,我们将项目放在队列中,以相同的顺序将项目从中出现。

通常,我们创建一个FIFO队列对象,如下所示:

q = queue.Queue()

示例:FIFO队列

#creating a FIFO queue
import queue
q = queue.Queue()
q.put(10)
q.put(5)
q.put(20)
q.put(15)
while not q.empty():
    print(q.get(),end=' ')

输出

10 5 20 15
  1. lifo队列| [最后一个]:

以相反的插入顺序进行删除的队列称为lifo队列。

#creating a LIFO queue
import queue
#creating LIFO queue object
q = queue.LifoQueue()
q.put(5)
q.put(10)
q.put(15)
q.put(20)
while not q.empty():
    print(q.get(),end=' ')

输出

py .\\LIFO.py
20 15 10 5
  1. 优先队列

将根据某些优先顺序插入元素的队列称为优先级队列。

示例:

#creating a Priority Queue object
import queue
q = queue.PriorityQueue()
q.put(5)
q.put(10)
q.put(15)
q.put(20)
while not q.empty():
    print(q.get(),end=' ')

输出

py .\\Priority.py
5 10 15 20

正如我们在上面的代码行中看到的,优先队列的行为是FIFO队列。

案例1:如果数据不是数字,则我们必须以tuple.ie

的形式提供我们的数据

(x,y)其中x是优先级,y是我们要插入的元素。

示例:

#creating a Priority Queue object
import queue
q = queue.PriorityQueue()
q.put((1,"apple"))
q.put((3,"ball"))
q.put((2,"bat"))
q.put((4,"cricket"))
while not q.empty():
    print(q.get()[1],end=' ')

输出

py .\\charac-Priority.py
apple bat ball cricket

在上面的代码中,我们看到最低的是值,最高的是优先级。

锁的编程实践

案例1

强烈建议编写释放锁定的代码,最后阻止。

优势是锁定始终释放,无论是否提出了例外,以及是否处理过。即

loc = threading.Lock()
#acquiring a lok
loc.acquire()
try:
    #performing some safe operation
finally:
    #releasing the lock
    loc.release()

示例:

#we should must release lock in finally block
from threading import *
import time
loc = Lock()
def wish(name):
    loc.acquire()
    try:
        for i in range(10):
            print("Good Morning:",end=" ")
            time.sleep(2)
            print(name)
    finally:
        loc.release()

t1 = Thread(target=wish,args=("Snehal",))
t2 = Thread(target=wish,args=("Apurva",))
t3 = Thread(target=wish,args=("Python",))
t1.start()
t2.start()
t3.start()

case-ii强烈建议通过使用语句获取锁。

语句的主要优点是,一旦控制块到达块的末端,锁定将自动释放,并且我们无需明确释放锁。

这与文件的用法完全相同。即

用open('demo.txt','w')作为text_file:

text_file.write(“你好这是我的文件”)

以相同的方式在锁中

lock = threading.Lock()
with lock:
        #perform required safe operation
        #lock will be released automatically

示例:

#using lock with 'with' statement
from threading import * 
import time
lock = Lock()
def wish(name):
    with lock:
        for i in range(5):
            print("Good Evening: ",end=" ")
            time.sleep(2)
            print(name)
t1 = Thread(target=wish,args=("Snehal",))
t2 = Thread(target=wish,args=("Apurva",))
t3 = Thread(target=wish,args=("Python",))
t1.start()
t2.start()
t3.start()

q。使用语句以获取线程锁定的优势是什么?

一旦控制到达块的末端,我们就不会明确释放锁定。

我们无需明确释放。

注意:我们可以与多线程中的语句一起使用以下情况:

  1. rlock

  2. 信号量

  3. 条件

这就是这个博客的终结!如果您喜欢内容,请跟随我以获取更多内容。如果您想要有关您感兴趣的任何主题的任何详细技术文档,请随时与之接触。到那时再见!继续编码继续学习。