python多线程传输_Python 多线程 threading

img

一个进程包含一个或多个线程

Thread 类

Thread 类表示在单独的控制线程中运行的活动。有两种方法来指定活动:通过将可调用对象传递给构造函数,或者通过重写子类中的 run() 方法。

普通

import time

def show():

print("咕嘿嘿~")

time.sleep(1)

if __name__ == '__main__':

sr = time.time()

for i in range(5):

show()

ed = time.time()

zw = ed - sr

print("耗时 %.3f" % zw)

root@H2o2:~/文档/PycharmProjects/进程与线程# python3 05-线程-threading.py

咕嘿嘿~

咕嘿嘿~

咕嘿嘿~

咕嘿嘿~

咕嘿嘿~

耗时 5.006

加入线程

import threading

import time

def show():

print("咕嘿嘿~")

time.sleep(1)

if __name__ == '__main__':

sr = time.time()

for i in range(5): # 5 个线程

t = threading.Thread(target=show)

t.start()

ed = time.time()

zw = ed - sr

print("耗时 %.3f" % zw)

root@H2o2:~/文档/PycharmProjects/进程与线程# python3 05-线程-threading.py

咕嘿嘿~

咕嘿嘿~

咕嘿嘿~

咕嘿嘿~

咕嘿嘿~

耗时 0.002

主线程等待所有子线程结束后才停止运行,emmm,是为了给子线程收尸

如果多个线程执行的都是同一个函数,各自之间是不会有影响的,各用各地。

Thread子类

如果子类覆盖了构造函数,它必须在对线程做任何其他事情之前调用基类构造函数(Thread.__init__())

import threading

import time

class NewThread(threading.Thread): # 与创建多进程的子类差不多

def __init__(self):

threading.Thread.__init__(self)

def run(self):

print("咕嘿嘿~%s" % self.name)

time.sleep(1)

if __name__ == '__main__':

sr = time.time()

for i in range(4):

t = NewThread()

t.start()

ed = time.time()

zw = ed - sr

print("共耗时: %.3f" % zw)

root@H2o2:~/文档/PycharmProjects/进程与线程# python3 06-线程-threading-子类.py

咕嘿嘿~Thread-1

咕嘿嘿~Thread-2

咕嘿嘿~Thread-3

咕嘿嘿~Thread-4

共耗时: 0.001

共享全局变量

进程中的资源数据都是互不影响的,线程之间共享全局变量

import threading

import time

num = 10

def addNum():

global num

num += 1

print("线程1--num = %d" % num)

def showNum():

global num

print("线程2--num = %d" % num)

if __name__ == '__main__':

print("线程创建前:num = %d" % num)

t1 = threading.Thread(target=addNum)

t1.start()

time.sleep(1)

t2 = threading.Thread(target=showNum)

t2.start()

root@H2o2:~/文档/PycharmProjects/进程与线程# python3 07-线程-全局变量共享.py

线程创建前:num = 10

线程1--num = 11

线程2--num = 11

线程都在同一个进程里,全局变量也在这个进程里,所以同一进程里的线程之间共享全局变量

所以就没有进程间的通信那么麻烦,都是共享的

线程共享全局变量的问题

import threading

import time

num = 0

def addNum():

global num

for i in range(100000):

num += 1

print("线程1--num = %d" % num)

def showNum():

global num

for i in range(100000):

num += 1

print("线程2--num = %d" % num)

if __name__ == '__main__':

print("线程创建前:num = %d" % num)

t1 = threading.Thread(target=addNum)

t1.start()

# time.sleep(1) # 注释掉

t2 = threading.Thread(target=showNum)

t2.start()

root@H2o2:~/文档/PycharmProjects/进程与线程# python3 08-线程-全局变量共享的问题.py

线程创建前:num = 0

线程1--num = 138376

线程2--num = 150962

两个线程都加 100000 理应出现一个 200000,但却一个也没有

将列表当做参数传递

import threading

import time

num = 0

def addNum(nums):

nums.append(4)

print("线程1--num = ", nums)

def showNum(nums):

time.sleep(1) # 等待1s,保证线程1完成

print("线程2--num = ", nums)

if __name__ == '__main__':

num = [1, 2, 3]

t1 = threading.Thread(target=addNum, args=(num,))

t1.start()

t2 = threading.Thread(target=showNum, args=(num,))

t2.start()

root@H2o2:~/文档/PycharmProjects/进程与线程# python3 09-线程-列表传递.py

线程1--num = [1, 2, 3, 4]

线程2--num = [1, 2, 3, 4]

在使用多线程的时候,数据共享是方便,但要确定结果是不是自己想要的

为什么上一个程序加了两次 100000,并没有得到 200000

操作系统不一定会将一条语句彻底执行完在执行下一条: num += 1,可能先执行完线程一的+=1后,再执行线程二的+=1,再执行线程一的num = 1,再线程二的num = 1,那么全局变量就还是 1,得不到预想的结果

解决方法1 -- 轮询

import threading

import time

num = 0

flage = 1

def addNum():

global num

global flage

if flage == 1:

for i in range(100000):

num += 1

flage = 0

print("线程1--num = %d" % num)

def showNum():

global num

while True:

if flage == 0:

for i in range(100000):

num += 1

break

print("线程2--num = %d" % num)

if __name__ == '__main__':

print("线程创建前:num = %d" % num)

t1 = threading.Thread(target=addNum)

t1.start()

t2 = threading.Thread(target=showNum)

t2.start()

root@H2o2:~/文档/PycharmProjects/进程与线程# python3 10-线程-解决全局变量共享问题.py

线程创建前:num = 0

线程1--num = 100000

线程2--num = 200000

虽然能解决,但是效率不高

解决方法2 -- 互斥锁

Lock 类

当多个线程几乎同时修改某一个共享数据时,需要进行同步控制,最简单的同步机制就是互斥锁(lock)。

from threading import Thread, Lock

import time

num = 0

def addNum():

global num

lk.acquire() # 上锁

for i in range(100000):

num += 1

lk.release() # 解锁

print("线程1--num = %d" % num)

def showNum():

global num

lk.acquire()  # 上锁

for i in range(100000):

num += 1

lk.release() # 解锁

print("线程2--num = %d" % num)

if __name__ == '__main__':

print("线程创建前:num = %d" % num)

lk = Lock() # 创建锁

t1 = Thread(target=addNum)

t1.start()

t2 = Thread(target=showNum)

t2.start()

root@H2o2:~/文档/PycharmProjects/进程与线程# python3 11-线程-解决全局变量共享问题-互斥锁.py

线程创建前:num = 0

线程1--num = 100000

线程2--num = 200000

Lock 默认是没有上锁的

无论哪一个线程先上锁,另一个便无法上锁,进行阻塞,等待解锁

Ps:在不修改只读取全局变量的时候,是不需要加锁的

非共享数据(局部)

import threading

import time

class NewClass(threading.Thread):

def __init__(self):

threading.Thread.__init__(self)

def run(self):

num = 10

print("当前是 %s 在执行" % self.name)

if self.name == "Thread-1":

num += 1

else:

time.sleep(2)

print("线程:%s ,num = %d" % (self.name, num))

if __name__ == '__main__':

lk = threading.Lock()

t1 = NewClass()

t1.start()

t2 = NewClass()

t2.start()

root@H2o2:~/文档/PycharmProjects/进程与线程# python3 12-线程-非共享数据.py

当前是 Thread-1 在执行

线程:Thread-1 ,num = 11

当前是 Thread-2 在执行

线程:Thread-2 ,num = 10

Thread-2的值并没有被改变,说明 不同线程执行同一函数时,其内的所有数据是独有的

不同线程间的全局变量是共享的,局部变量是私有的

各线程间的非共享数据是独立的,也就不需要加锁了

死锁

在线程共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁

import threading

import time

class NewClass(threading.Thread):

def run(self):

if lkA.acquire(): # 默认为开锁,正常执行后上锁

print(self.name + "---do1--up---")

time.sleep(1)

if lkB.acquire(): # 等待 lkB 解锁

print(self.name + "---do2--down---")

lkB.release()

lkA.release()

class NewClassTwo(threading.Thread):

def run(self):

if lkB.acquire(): # 默认为开锁,正常执行后上锁

print(self.name + "---do2--up---")

time.sleep(1)

if lkA.acquire(): # 等待 lkA 解锁

print(self.name + "---do2--down---")

lkA.release()

lkB.release()

if __name__ == '__main__':

lkA = threading.Lock()

lkB = threading.Lock()

t1 = NewClass()

t2 = NewClassTwo()

t1.start()

t2.start()

root@H2o2:~/文档/PycharmProjects/进程与线程# python3 13-死锁.py

Thread-1---do1--up---

Thread-2---do1--up---

|   # 持续等待

lkA在等待 lkB 解锁,lkB又在等待 lkA 解锁,相互僵持,形成死锁

避免死锁

程序设计尽量避开(银行家算法)

上锁时添加超时时间 acquire(blocking=True, timeout=-1)

同步

同步就是协调步调,按预定的先后次序进行运行。如:你先说,我再说

这个同不是一起执行,而是协同、协助、相互配合。如进程、线程同步,可理解为进程或线程A与B一块配合,A执行的一定程度时依靠B的某个结果,于是停下来,示意B运行,再将结果给A,A继续运行

import threading

import time

class NewClassT1(threading.Thread):

def run(self):

while True:

if lkA.acquire():

print("---AAA---")

time.sleep(1)

lkB.release()

class NewClassT2(threading.Thread):

def run(self):

while True:

if lkB.acquire():

print("---BBB---")

time.sleep(1)

lkC.release()

class NewClassT3(threading.Thread):

def run(self):

while True:

if lkC.acquire():

print("---CCC---")

time.sleep(1)

lkA.release()

if __name__ == '__main__':

lkA = threading.Lock()

lkB = threading.Lock()

lkB.acquire()

lkC = threading.Lock()

lkC.acquire()

t1 = NewClassT1()

t2 = NewClassT2()

t3 = NewClassT3()

t1.start()

t2.start()

t3.start()

root@H2o2:~/文档/PycharmProjects/进程与线程# python3 14-同步的应用.py

---AAA---

---BBB---

---CCC---

---AAA---

---BBB---

---CCC---

---AAA---

---BBB---

---CCC---

---AAA---

---BBB---

...

生产者与消费者模式(数据生产与数据处理)

在做爬虫的时候,会遇到爬去的数据跟不上处理速度或处理速度跟不上爬取速度,会堆积在那里,所造成一些问题,就需要在爬取与处理间放一个缓存来解决,可以用Queue来解决

Queue类

python2 导入方式: from Queue import Queue

python3 导入方式: from queue import Queue

import threading

import time

from queue import Queue

class NewClassT1(threading.Thread):

def run(self):

global queue

count = 0

while True:

if queue.qsize() < 1000:

for i in range(100):

count = count + 1

msg = "生成了:" + str(count)

queue.put(msg)

print(msg)

time.sleep(0.5)

class NewClassT2(threading.Thread):

def run(self):

global queue

while True:

if queue.qsize() > 100:

for i in range(3):

msg = self.name + "处理了:" + queue.get()

print(msg)

time.sleep(1)

if __name__ == '__main__':

queue = Queue()

for i in range(500):

queue.put("初始添加数据:" + str(i))

for i in range(2):

t1 = NewClassT1()

t1.start()

for i in range(5):

t2 = NewClassT2()

t2.start()

root@H2o2:~/文档/PycharmProjects/进程与线程# python3 15-生产者与消费者模式.py

生成了:1

生成了:2

生成了:3

生成了:4

生成了:5

生成了:6

生成了:1

Thread-3处理了:初始添加数据:0

Thread-3处理了:初始添加数据:3

Thread-5处理了:初始添加数据:2

Thread-4处理了:初始添加数据:1

生成了:2

生成了:7

Thread-6处理了:初始添加数据:6

Thread-4处理了:初始添加数据:7

生成了:8

生成了:9

Thread-6处理了:初始添加数据:8

生成了:3

生成了:4

Thread-3处理了:初始添加数据:4

Thread-5处理了:初始添加数据:5

Thread-5处理了:初始添加数据:12

Thread-6处理了:初始添加数据:11

生成了:10

Thread-4处理了:初始添加数据:9

Thread-7处理了:初始添加数据:10

生成了:11

Thread-7处理了:初始添加数据:13

生成了:12

Thread-7处理了:初始添加数据:14

生成了:13

生成了:14

生成了:5

生成了:15

生成了:6

生成了:16

...

ThreadLocal

多个线程访问同一个函数,函数内(局部)的数据是独立互不影响的

import threading

import time

def run():

num = 100

num += 1

print(num)

if __name__ =='__main__':

t1 = threading.Thread(target=run)

t2 = threading.Thread(target=run)

t1.start()

time.sleep(2)

t2.start()

h2o2@h2o2-PC:~/Documents/01-工程文件/PycharmProjects/04-进程与线程$ python3 16-线程-ThreadLocal对象.py

101

101

A函数要得到B函数的值,要么返回值,要么全局变量,在线程中,两种方式要么用不了,要么太枯燥麻烦。这时候可以用全局字典

import threading

import time

global_dict = {}

def run_th(num):

global_dict[threading.current_thread()] = num

cc = global_dict[threading.current_thread()]

print("线程:%s -- 值:%s" % (threading.current_thread().name, cc))

do_run_1()

do_run_2()

def do_run_1():

th = threading.current_thread().name

vul_1 = global_dict[threading.current_thread()]

print("当前线程是:%s -- 值:%s" % (th, vul_1))

def do_run_2():

th = threading.current_thread().name

vul_1 = global_dict[threading.current_thread()]

print("当前线程是:%s -- 值:%s" % (th, vul_1))

if __name__ == '__main__':

t1 = threading.Thread(target=run_th, args=(1,))

t2 = threading.Thread(target=run_th, args=(2,))

t1.start()

# time.sleep(2)

t2.start()

h2o2@h2o2-PC:~/Documents/01-工程文件/PycharmProjects/04-进程与线程$ python3 17-线程-全局字典传值.py

线程:Thread-1 -- 值:1

当前线程是:Thread-1 -- 值:1

当前线程是:Thread-1 -- 值:1

线程:Thread-2 -- 值:2

当前线程是:Thread-2 -- 值:2

当前线程是:Thread-2 -- 值:2

全局字典也麻烦,不太合适..最好、最简单的方法还是ThreadLocal

Thread-local数据是其值是线程特定的数据。

无论有多少线程,threadlocal的值都是各自线程的值,不会因为下一个线程将threadlocal值修改后而改变上一个线程的threadlocal值

import threading

def do_run_1():

st = local_1.number

print("线程:%s -- 值:%s" % (threading.current_thread().name, st))

def run_th(num):

local_1.number = num # 绑定local_1 的属性number 的值 num

do_run_1()

if __name__ == '__main__':

# 创建全局threadlocal 对象

local_1 = threading.local()

t1 = threading.Thread(target=run_th, args=(1,), name="T-A")

t2 = threading.Thread(target=run_th, args=(2,), name="T-B")

t1.start()

t2.start()

t1.join()

t2.join()

h2o2@h2o2-PC:~/Documents/01-工程文件/PycharmProjects/04-进程与线程$ python3 18-线程-ThreadLocal.py

线程:T-A -- 值:1

线程:T-B -- 值:2

异步

import time

from multiprocessing import Pool

import os

def do_run_1():

print("---线程池中的进程的 pid:%d,ppid:%d--" % (os.getpid(), os.getppid()))

for i in range(3):

print("---%d---" % i)

time.sleep(1)

return "FFF"

def do_run_2(args):

print("回调函数--pid=%d" % os.getpid())

print("回调函数返回值=%s" % args)

if __name__ == '__main__':

pool = Pool(3)

pool.apply_async(func=do_run_1, callback=do_run_2) # 进程池中加入一个进程

time.sleep(5) # 子进程结束后,回调函数由 父进程操作

print("---主线程--pid=%d ---" % os.getpid()) # 主进程在子进程结束后,暂停自己的事情去执行回调函数,后执行自己的任务

h2o2@h2o2-PC:~/Documents/01-工程文件/PycharmProjects/04-进程与线程$ python3 19-异步.py

---线程池中的进程的 pid:7987,ppid:7986--

---0---

---1---

---2---

回调函数--pid=7986

回调函数返回值=FFF

---主线程--pid=7986 ---

GIL(全局解释器锁)

同步:你吃饭吃到一半,电话来了,你一直到吃完了以后才去接,说明不支持并发也不支持并行

异步:你吃饭吃到一半,电话来了,你停了下来接了电话,接完后电话以后继续吃饭,说明支持并发

你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行

并发:交替处理多个任务的能力;执行的任务大于核数

并行:同时处理多个任务的能力;执行的任务小于核数

并发的关键是你有处理多个任务的能力,不一定要同时

并行的关键是你有同时处理多个任务的能力,强调的是同时.

所以它们最大的区别就是:是否是『同时』处理任务。

对于一个多核cpu来说并行要比并发快的多

cpython解释器中存在一个GIL(全局解释器锁),它的作用就是保证同一时刻只有一个线程可以执行代码,因此造成了我们使用多线程的时候无法实现并行。

多核CPU最好的多任务选择还是多进程,多进程的效率远远大于多进程

解决方式

使用多进程

若是必须用 多线程,关键地方可以用 C 语言解决

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值