当前位置:首页 » 编程语言 » python多线程threading

python多线程threading

发布时间: 2022-10-04 12:36:05

python多线程的几种方法

Python进阶(二十六)-多线程实现同步的四种方式
临界资源即那些一次只能被一个线程访问的资源,典型例子就是打印机,它一次只能被一个程序用来执行打印功能,因为不能多个线程同时操作,而访问这部分资源的代码通常称之为临界区。
锁机制
threading的Lock类,用该类的acquire函数进行加锁,用realease函数进行解锁
import threadingimport timeclass Num:
def __init__(self):
self.num = 0
self.lock = threading.Lock() def add(self):
self.lock.acquire()#加锁,锁住相应的资源
self.num += 1
num = self.num
self.lock.release()#解锁,离开该资源
return num

n = Num()class jdThread(threading.Thread):
def __init__(self,item):
threading.Thread.__init__(self)
self.item = item def run(self):
time.sleep(2)
value = n.add()#将num加1,并输出原来的数据和+1之后的数据
print(self.item,value)for item in range(5):
t = jdThread(item)
t.start()
t.join()#使线程一个一个执行

当一个线程调用锁的acquire()方法获得锁时,锁就进入“locked”状态。每次只有一个线程可以获得锁。如果此时另一个线程试图获得这个锁,该线程就会变为“blocked”状态,称为“同步阻塞”(参见多线程的基本概念)。
直到拥有锁的线程调用锁的release()方法释放锁之后,锁进入“unlocked”状态。线程调度程序从处于同步阻塞状态的线程中选择一个来获得锁,并使得该线程进入运行(running)状态。
信号量
信号量也提供acquire方法和release方法,每当调用acquire方法的时候,如果内部计数器大于0,则将其减1,如果内部计数器等于0,则会阻塞该线程,知道有线程调用了release方法将内部计数器更新到大于1位置。
import threadingimport timeclass Num:
def __init__(self):
self.num = 0
self.sem = threading.Semaphore(value = 3) #允许最多三个线程同时访问资源

def add(self):
self.sem.acquire()#内部计数器减1
self.num += 1
num = self.num
self.sem.release()#内部计数器加1
return num

n = Num()class jdThread(threading.Thread):
def __init__(self,item):
threading.Thread.__init__(self)
self.item = item def run(self):
time.sleep(2)
value = n.add()
print(self.item,value)for item in range(100):

⑵ python中的多线程为什么会报错

题主你好,
你的问题在于threading模式的Thread()类使用的不正确,你代码中的代码为:
t1 = threading.Thread(target=name_b, name="job2")
其中name_b是你定义的函数名, 其实光看语句本身是没错的,但问题出在:
" name_b这个函数定义中有两个参数,你没有在Thread()类中给name_b传参 "
你想一下,如果不考虑多线程,你去调用name_b这个函数,光写个:
name_b() 肯定是不对的
你要将os_name2和url_b这两个参数也带上才行,即:
name_b(xxx, yyy)

所以就你当前代码报错信息要改的话, 只需要在两个threading.Thread()中再加一个args参数,将传给函数的参数代码写在" () "中, 即:
threading.Thread(target=name_b, name="job2", args=(xxx, yyy))
另一个threading.Thread()你照着上面的写即可.
希望可以帮到题主, 欢迎追问

⑶ Python面试题,线程与进程的区别,Python中如何创建多线程

进程和线程

这两个概念属于操作系统,我们经常听说,但是可能很少有人会细究它们的含义。对于工程师而言,两者的定义和区别还是很有必要了解清楚的。

首先说进程,进程可以看成是 CPU执行的具体的任务 。在操作系统当中,由于CPU的运行速度非常快,要比计算机当中的其他设备要快得多。比如内存、磁盘等等,所以如果CPU一次只执行一个任务,那么会导致CPU大量时间在等待这些设备,这样操作效率很低。为了提升计算机的运行效率,把机器的技能尽可能压榨出来,CPU是轮询工作的。也就是说 它一次只执行一个任务,执行一小段碎片时间之后立即切换 ,去执行其他任务。

所以在早期的单核机器的时候,看起来电脑也是并发工作的。我们可以一边听歌一边上网,也不会觉得卡顿。但实际上,这是CPU轮询的结果。在这个例子当中,听歌的软件和上网的软件对于CPU而言都是 独立的进程 。我们可以把进程简单地理解成运行的应用,比如在安卓手机里面,一个app启动的时候就会对应系统中的一个进程。当然这种说法不完全准确, 一个应用也是可以启动多个进程的

进程是对应CPU而言的,线程则更多针对的是程序。即使是CPU在执行当前进程的时候,程序运行的任务其实也是有分工的。举个例子,比如听歌软件当中,我们需要显示歌词的字幕,需要播放声音,需要监听用户的行为,比如是否发生了切歌、调节音量等等。所以,我们需要 进一步拆分CPU的工作 ,让它在执行当前进程的时候,继续通过轮询的方式来同时做多件事情。

进程中的任务就是线程,所以从这点上来说, 进程和线程是包含关系 。一个进程当中可以包含多个线程,对于CPU而言,不能直接执行线程,一个线程一定属于一个进程。所以我们知道,CPU进程切换切换的是执行的应用程序或者是软件,而进程内部的线程切换,切换的是软件当中具体的执行任务。

关于进程和线程有一个经典的模型可以说明它们之间的关系,假设CPU是一家工厂,工厂当中有多个车间。不同的车间对应不同的生产任务,有的车间生产汽车轮胎,有的车间生产汽车骨架。但是工厂的电力是有限的,同时只能满足一个厂房的使用。

为了让大家的进度协调,所以工厂需要轮流提供各个车间的供电。 这里的车间对应的就是进程

一个车间虽然只生产一种产品,但是其中的工序却不止一个。一个车间可能会有好几条流水线,具体的生产任务其实是流水线完成的,每一条流水线对应一个具体执行的任务。但是同样的, 车间同一时刻也只能执行一条流水线 ,所以我们需要车间在这些流水线之间切换供电,让各个流水线生产进度统一。

这里车间里的 流水线自然对应的就是线程的概念 ,这个模型很好地诠释了CPU、进程和线程之间的关系。实际的原理也的确如此,不过CPU中的情况要比现实中的车间复杂得多。因为对于进程和CPU来说,它们面临的局面都是实时变化的。车间当中的流水线是x个,下一刻可能就成了y个。

了解完了线程和进程的概念之后,对于理解电脑的配置也有帮助。比如我们买电脑,经常会碰到一个术语,就是这个电脑的CPU是某某核某某线程的。比如我当年买的第一台笔记本是4核8线程的,这其实是在说这台电脑的CPU有 4个计算核心 ,但是使用了超线程技术,使得可以把一个物理核心模拟成两个逻辑核心。相当于我们可以用4个核心同时执行8个线程,相当于8个核心同时执行,但其实有4个核心是模拟出来的虚拟核心。

有一个问题是 为什么是4核8线程而不是4核8进程呢 ?因为CPU并不会直接执行进程,而是执行的是进程当中的某一个线程。就好像车间并不能直接生产零件,只有流水线才能生产零件。车间负责的更多是资源的调配,所以教科书里有一句非常经典的话来诠释: 进程是资源分配的最小单元,线程是CPU调度的最小单元

启动线程

Python当中为我们提供了完善的threading库,通过它,我们可以非常方便地创建线程来执行多线程。

首先,我们引入threading中的Thread,这是一个线程的类,我们可以通过创建一个线程的实例来执行多线程。

from threading import Thread t = Thread(target=func, name='therad', args=(x, y)) t.start()

简单解释一下它的用法,我们传入了三个参数,分别是 target,name和args ,从名字上我们就可以猜测出它们的含义。首先是target,它传入的是一个方法,也就是我们希望多线程执行的方法。name是我们为这个新创建的线程起的名字,这个参数可以省略,如果省略的话,系统会为它起一个系统名。当我们执行Python的时候启动的线程名叫MainThread,通过线程的名字我们可以做区分。args是会传递给target这个函数的参数。

我们来举个经典的例子:

import time, threading # 新线程执行的代码: def loop(n): print('thread %s is running...' % threading.current_thread().name) for i in range(n): print('thread %s >>> %s' % (threading.current_thread().name, i)) time.sleep(5) print('thread %s ended.' % threading.current_thread().name) print('thread %s is running...' % threading.current_thread().name) t = threading.Thread(target=loop, name='LoopThread', args=(10, )) t.start() print('thread %s ended.' % threading.current_thread().name)

我们创建了一个非常简单的loop函数,用来执行一个循环来打印数字,我们每次打印一个数字之后这个线程会睡眠5秒钟,所以我们看到的结果应该是每过5秒钟屏幕上多出一行数字。

我们在Jupyter里执行一下:

表面上看这个结果没毛病,但是其实有一个问题,什么问题呢? 输出的顺序不太对 ,为什么我们在打印了第一个数字0之后,主线程就结束了呢?另外一个问题是,既然主线程已经结束了, 为什么Python进程没有结束 , 还在向外打印结果呢?

因为线程之间是独立的,对于主线程而言,它在执行了t.start()之后,并 不会停留,而是会一直往下执行一直到结束 。如果我们不希望主线程在这个时候结束,而是阻塞等待子线程运行结束之后再继续运行,我们可以在代码当中加上t.join()这一行来实现这点。

t.start() t.join() print('thread %s ended.' % threading.current_thread().name)

join操作可以让主线程在join处挂起等待,直到子线程执行结束之后,再继续往下执行。我们加上了join之后的运行结果是这样的:

这个就是我们预期的样子了,等待子线程执行结束之后再继续。

我们再来看第二个问题,为什么主线程结束的时候,子线程还在继续运行,Python进程没有退出呢?这是因为默认情况下我们创建的都是用户级线程,对于进程而言, 会等待所有用户级线程执行结束之后才退出 。这里就有了一个问题,那假如我们创建了一个线程尝试从一个接口当中获取数据,由于接口一直没有返回,当前进程岂不是会永远等待下去?

这显然是不合理的,所以为了解决这个问题,我们可以把创建出来的线程设置成 守护线程

守护线程

守护线程即daemon线程,它的英文直译其实是后台驻留程序,所以我们也可以理解成 后台线程 ,这样更方便理解。daemon线程和用户线程级别不同,进程不会主动等待daemon线程的执行, 当所有用户级线程执行结束之后即会退出。进程退出时会kill掉所有守护线程

我们传入daemon=True参数来将创建出来的线程设置成后台线程:

t = threading.Thread(target=loop, name='LoopThread', args=(10, ), daemon=True)

这样我们再执行看到的结果就是这样了:

这里有一点需要注意,如果你 在jupyter当中运行是看不到这样的结果的 。因为jupyter自身是一个进程,对于jupyter当中的cell而言,它一直是有用户级线程存活的,所以进程不会退出。所以想要看到这样的效果,只能通过命令行执行Python文件。

如果我们想要等待这个子线程结束,就必须通过join方法。另外,为了预防子线程锁死一直无法退出的情况, 我们还可以 在joih当中设置timeout ,即最长等待时间,当等待时间到达之后,将不再等待。

比如我在join当中设置的timeout等于5时,屏幕上就只会输出5个数字。

另外,如果没有设置成后台线程的话,设置timeout虽然也有用,但是 进程仍然会等待所有子线程结束 。所以屏幕上的输出结果会是这样的:

虽然主线程继续往下执行并且结束了,但是子线程仍然一直运行,直到子线程也运行结束。

关于join设置timeout这里有一个坑,如果我们只有一个线程要等待还好,如果有多个线程,我们用一个循环将它们设置等待的话。那么 主线程一共会等待N * timeout的时间 ,这里的N是线程的数量。因为每个线程计算是否超时的开始时间是上一个线程超时结束的时间,它会等待所有线程都超时,才会一起终止它们。

比如我这样创建3个线程:

ths = [] for i in range(3): t = threading.Thread(target=loop, name='LoopThread' + str(i), args=(10, ), daemon=True) ths.append(t) for t in ths: t.start() for t in ths: t.join(2)

最后屏幕上输出的结果是这样的:

所有线程都存活了6秒。

总结

在今天的文章当中,我们一起简单了解了 操作系统当中线程和进程的概念 ,以及Python当中如何创建一个线程,以及关于创建线程之后的相关使用。

多线程在许多语言当中都是至关重要的,许多场景下必定会使用到多线程。比如 web后端,比如爬虫,再比如游戏开发 以及其他所有需要涉及开发ui界面的领域。因为凡是涉及到ui,必然会需要一个线程单独渲染页面,另外的线程负责准备数据和执行逻辑。因此,多线程是专业程序员绕不开的一个话题,也是一定要掌握的内容之一。

⑷ python 多线程和多进程的区别 mutiprocessing theading

在socketserver服务端代码中有这么一句:

server = socketserver.ThreadingTCPServer((ip,port), MyServer)

ThreadingTCPServer这个类是一个支持多线程和TCP协议的socketserver,它的继承关系是这样的:

class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass

右边的TCPServer实际上是主要的功能父类,而左边的ThreadingMixIn则是实现了多线程的类,ThreadingTCPServer自己本身则没有任何代码。

MixIn在Python的类命名中很常见,称作“混入”,戏称“乱入”,通常为了某种重要功能被子类继承。

我们看看一下ThreadingMixIn的源代码:

class ThreadingMixIn:

daemon_threads = False

def process_request_thread(self, request, client_address):
try:
self.finish_request(request, client_address)
self.shutdown_request(request)
except:
self.handle_error(request, client_address)
self.shutdown_request(request)

def process_request(self, request, client_address):

t = threading.Thread(target = self.process_request_thread,
args = (request, client_address))
t.daemon = self.daemon_threads
t.start()

在ThreadingMixIn类中,其实就定义了一个属性,两个方法。其中的process_request()方法实际调用的正是Python内置的多线程模块threading。这个模块是Python中所有多线程的基础,socketserver本质上也是利用了这个模块。

socketserver通过threading模块,实现了多线程任务处理能力,可以同时为多个客户提供服务。

那么,什么是线程,什么是进程?

进程是程序(软件,应用)的一个执行实例,每个运行中的程序,可以同时创建多个进程,但至少要有一个。每个进程都提供执行程序所需的所有资源,都有一个虚拟的地址空间、可执行的代码、操作系统的接口、安全的上下文(记录启动该进程的用户和权限等等)、唯一的进程ID、环境变量、优先级类、最小和最大的工作空间(内存空间)。进程可以包含线程,并且每个进程必须有至少一个线程。每个进程启动时都会最先产生一个线程,即主线程,然后主线程会再创建其他的子线程。

线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不独立拥有系统资源,但它可与同属一个进程的其它线程共享该进程所拥有的全部资源。每一个应用程序都至少有一个进程和一个线程。在单个程序中同时运行多个线程完成不同的被划分成一块一块的工作,称为多线程。

举个例子,某公司要生产一种产品,于是在生产基地建设了很多厂房,每个厂房内又有多条流水生产线。所有厂房配合将整个产品生产出来,单个厂房内的流水线负责生产所属厂房的产品部件,每个厂房都拥有自己的材料库,厂房内的生产线共享这些材料。公司要实现生产必须拥有至少一个厂房一条生产线。换成计算机的概念,那么这家公司就是应用程序,厂房就是应用程序的进程,生产线就是某个进程的一个线程。

线程的特点:

线程是一个execution context(执行上下文),即一个cpu执行时所需要的一串指令。假设你正在读一本书,没有读完,你想休息一下,但是你想在回来时继续先前的进度。有一个方法就是记下页数、行数与字数这三个数值,这些数值就是execution context。如果你的室友在你休息的时候,使用相同的方法读这本书。你和她只需要这三个数字记下来就可以在交替的时间共同阅读这本书了。

线程的工作方式与此类似。CPU会给你一个在同一时间能够做多个运算的幻觉,实际上它在每个运算上只花了极少的时间,本质上CPU同一时刻只能干一件事,所谓的多线程和并发处理只是假象。CPU能这样做是因为它有每个任务的execution context,就像你能够和你朋友共享同一本书一样。

进程与线程区别:

  • 同一个进程中的线程共享同一内存空间,但进程之间的内存空间是独立的。

  • 同一个进程中的所有线程的数据是共享的,但进程之间的数据是独立的。

  • 对主线程的修改可能会影响其他线程的行为,但是父进程的修改(除了删除以外)不会影响其他子进程。

  • 线程是一个上下文的执行指令,而进程则是与运算相关的一簇资源。

  • 同一个进程的线程之间可以直接通信,但是进程之间的交流需要借助中间代理来实现。

  • 创建新的线程很容易,但是创建新的进程需要对父进程做一次复制。

  • 一个线程可以操作同一进程的其他线程,但是进程只能操作其子进程。

  • 线程启动速度快,进程启动速度慢(但是两者运行速度没有可比性)。

  • 由于现代cpu已经进入多核时代,并且主频也相对以往大幅提升,多线程和多进程编程已经成为主流。Python全面支持多线程和多进程编程,同时还支持协程。

⑸ python threadingd多线程老是出错

把你的代码执行了一下,应该是有两个地方有问题。

1、入参(u'飒飒',)这个改成(u'飒飒'.encode("utf-8"),)

2、t2=threading.Thread(target=movie,args=(u'问问')),这个地方改成

t2=threading.Thread(target=movie,args=(u'问问',))。不然会当成两个参数。


3、第三个不确定你的用途。就是t.setDaemon(True)那个地方。调用setDaemon的时候,子线程会随着主线程一起结束,不管子线程是否执行完成。所以有时候会出现执行完了啥输出都没有的情况。如果想让主线程等待子线程,在start后面使用t.join()。这样就会一直都有输出了。


修改完如下:

#coding=utf-8
importthreading
fromtimeimportctime

defmusic(m):
print'在听%s,现在是%s'%(m,ctime())

defmovie(mo):
print'在看%s,现在是%s'%(mo,ctime())

threadList=[]
t1=threading.Thread(target=music,args=(u'飒飒'.encode("utf-8"),))
threadList.append(t1)

t2=threading.Thread(target=movie,args=(u'问问'.encode("utf-8")))
threadList.append(t2)

fortinthreadList:
t.setDaemon(True)
t.start()
#t.join()

⑹ 请教python如何开启多线程

可以定义函数把这些代码放在不同的函数里,然后threading模块
import threading
th1 = threading.Thread(target=func1, args=(arg1, arg2, ...))
照这样再定义别的线程,开启用Thread类的start方法
th1.start(); th2.start(); ...

⑺ Python多线程总结

在实际处理数据时,因系统内存有限,我们不可能一次把所有数据都导出进行操作,所以需要批量导出依次操作。为了加快运行,我们会采用多线程的方法进行数据处理, 以下为我总结的多线程批量处理数据的模板:

主要分为三大部分:


共分4部分对多线程的内容进行总结。

先为大家介绍线程的相关概念:

在飞车程序中,如果没有多线程,我们就不能一边听歌一边玩飞车,听歌与玩 游戏 不能并行;在使用多线程后,我们就可以在玩 游戏 的同时听背景音乐。在这个例子中启动飞车程序就是一个进程,玩 游戏 和听音乐是两个线程。

Python 提供了 threading 模块来实现多线程:

因为新建线程系统需要分配资源、终止线程系统需要回收资源,所以如果可以重用线程,则可以减去新建/终止的开销以提升性能。同时,使用线程池的语法比自己新建线程执行线程更加简洁。

Python 为我们提供了 ThreadPoolExecutor 来实现线程池,此线程池默认子线程守护。它的适应场景为突发性大量请求或需要大量线程完成任务,但实际任务处理时间较短。

其中 max_workers 为线程池中的线程个数,常用的遍历方法有 map 和 submit+as_completed 。根据业务场景的不同,若我们需要输出结果按遍历顺序返回,我们就用 map 方法,若想谁先完成就返回谁,我们就用 submit+as_complete 方法。

我们把一个时间段内只允许一个线程使用的资源称为临界资源,对临界资源的访问,必须互斥的进行。互斥,也称间接制约关系。线程互斥指当一个线程访问某临界资源时,另一个想要访问该临界资源的线程必须等待。当前访问临界资源的线程访问结束,释放该资源之后,另一个线程才能去访问临界资源。锁的功能就是实现线程互斥。

我把线程互斥比作厕所包间上大号的过程,因为包间里只有一个坑,所以只允许一个人进行大号。当第一个人要上厕所时,会将门上上锁,这时如果第二个人也想大号,那就必须等第一个人上完,将锁解开后才能进行,在这期间第二个人就只能在门外等着。这个过程与代码中使用锁的原理如出一辙,这里的坑就是临界资源。 Python 的 threading 模块引入了锁。 threading 模块提供了 Lock 类,它有如下方法加锁和释放锁:

我们会发现这个程序只会打印“第一道锁”,而且程序既没有终止,也没有继续运行。这是因为 Lock 锁在同一线程内第一次加锁之后还没有释放时,就进行了第二次 acquire 请求,导致无法执行 release ,所以锁永远无法释放,这就是死锁。如果我们使用 RLock 就能正常运行,不会发生死锁的状态。

在主线程中定义 Lock 锁,然后上锁,再创建一个子 线程t 运行 main 函数释放锁,结果正常输出,说明主线程上的锁,可由子线程解锁。

如果把上面的锁改为 RLock 则报错。在实际中设计程序时,我们会将每个功能分别封装成一个函数,每个函数中都可能会有临界区域,所以就需要用到 RLock 。

一句话总结就是 Lock 不能套娃, RLock 可以套娃; Lock 可以由其他线程中的锁进行操作, RLock 只能由本线程进行操作。

⑻ python threading 多线程怎么用

使用threading.Thread(),实例化一个线程
#-*-coding:utf-8-*-
importthreading

#使用threading.Thread(),实例化一个线程
defT():
printthreading.current_thread().getName()

#创建线程对象
t1=threading.Thread(target=T,name='tt11')
#启动线程
t1.start()
t1.join()

⑼ python 怎么实现多线程的

线程也就是轻量级的进程,多线程允许一次执行多个线程,Python是多线程语言,它有一个多线程包,GIL也就是全局解释器锁,以确保一次执行单个线程,一个线程保存GIL并在将其传递给下一个线程之前执行一些操作,也就产生了并行执行的错觉。

⑽ python 循环中使用多线程

import time
import threading
def p(*listp):
for i in range(0,len(listp)):
print (listp[i])
p1=[]
p2=[]
for i in range(1,100):
if(i%10==1):
p1.append(i)
elif(i%10==2):
p2.append(i)
threads=[]
th1=threading.Thread(target=p,args=(p1))
threads.append(th1)
th2=threading.Thread(target=p,args=(p2))
threads.append(th2)

if __name__=='__main__':
for t in threads:
t.setDaemon(True)
t.start()




热点内容
数据库说明文档 发布:2024-12-23 12:22:12 浏览:620
安卓手机玩mc卡怎么办 发布:2024-12-23 12:15:46 浏览:5
mt编译时出现错误信息 发布:2024-12-23 12:15:45 浏览:107
双存储冗余 发布:2024-12-23 12:09:16 浏览:664
解压缩太慢 发布:2024-12-23 12:08:36 浏览:535
linux恢复误删文件 发布:2024-12-23 11:59:36 浏览:493
平板电脑账号登录服务器错误 发布:2024-12-23 11:41:07 浏览:99
金蝶kis专业版数据库表 发布:2024-12-23 11:35:41 浏览:602
相册已经加密如何改密码 发布:2024-12-23 11:32:20 浏览:277
批量下载链接脚本 发布:2024-12-23 11:29:11 浏览:973