python网络线程

线程

线程是一种多任务编程的方式,可以使用计算机多核资源。线程又被称为轻量级的进程

线程特征

* 线程是计算机核心分配的最小单位

* 一个进程可以包含多个线程

* 线程也是一个运行过程,也要消耗计算机资源。多个线程共享其进程的资源和空间

* 线程也拥有自己特有的资源属性,比如指令集,TID等

* 线程无论创建还是删除还是运行资源消耗都小于进程

* 多个线程之间并行执行,互不干扰

threading线程模块

from threading import Thread

t = Thread(target, [, args], [kwargs])

创建线程对象

  • target 绑定线程函数
  • args  元组 给线程函数位置传参
  • kwargs 字典 给线程函数键值传参

t.start() 启动线程

t.join([timeout]) 回收线程

import threading
import os

a = 1

# 线程函数
def music():
  print("进程pid号", os.getpid())
  global a
  print("a = ",a)
  a = 10000

t = threading.Thread(target=music)    # 创建线程对象
t.start()                               # 启动线程

print("进程pid号", os.getpid())

t.join()                                # 回收线程

print("Main a:",a)

# 进程pid号 12549
# 进程pid号 12549
# a =  1
# Main a: 10000

os.getpid获取的是进程的pid号,线程是进程中的一个成员.

线程中改的变量,是进程中的变量.并没有新开辟一个空间.

线程属性

t.is_alive()查看线程状态

t.name线程名称默认Thread-1

t.setName()设置线程名称

threading.currentThread()获取当前线程对象


 1 from threading import Thread,currentThread
 2 from time import sleep 
 3 
 4 #线程函数
 5 def fun(sec):
 6     print("线程属性测试")
 7     sleep(sec)
 8     #获取线程对象 getName()获取名字
 9     print("%s 线程结束"%currentThread().getName())
10 
11 thread = []
12 
13 for i in range(3):
14     t = Thread(target = fun,name = "tedu%d"%i,\
15         args = (3,))
16     thread.append(t)
17     t.start()
18     print(t.is_alive())  #查看进程状态
19 
20 thread[1].setName('Tarena')  #设置线程名称
21 print(thread[2].name) #获取线程名称
22 
23 #回收线程
24 for i in thread:
25     i.join()
26 
27 # 线程属性测试
28 # True
29 # 线程属性测试
30 # True
31 # 线程属性测试
32 # True
33 # tedu2
34 # Tarena 线程结束
35 # tedu0 线程结束
36 # tedu2 线程结束

View Code

t.daemon

默认情况下,主线程的结束不会影响分支线程,如果设置为True则主线程退出分支线程也会退出

设置方法:

t.daemon = True

t.setDaemon()

线程daemon属性的设置在start前;一般设置daemon后不会使用join

from threading import Thread
from time import sleep

def fun():
  sleep(3)
  print("线程属性测试")

t = Thread(target=fun, name = "Tarena")

# 主线程退出分支线程也退出
t.setDaemon(True)

t.start()

t.setName("Tedu")
print("Name:",t.getName())  # 线程名称
print("Alive:",t.is_alive())  # 线程生命周期
print("is Daemon",t.isDaemon()) # 主进程随着分支进程退出

自定义线程类

  1. 继承Thread类
  2. 运行Thread类中的__init__方法以获取父类属性
  3. 重写run方法

使用方法

  1. 实例化对象
  2. 调用start自动化执行run方法
  3. 调用join回收线程
from threading import Thread

class ThreadClass(Thread):
  # 重写父类init
  def __init__(self, *args, **kwargs):
    self.attr = args[0]
    super().__init__()    # 加载父类init

  def fun1(self):
    print("函数1")

  def fun2(self):
    print("函数2")

  # 重写run,逻辑调用
  def run(self):
    self.fun1()
    self.fun2()


t = ThreadClass("abc")
t.start()
t.join()

# 函数1
# 函数2

同步互斥

线程间通信方法

1.通信方法:线程间使用全局变量进行通信

2. 共享资源争夺

  • 共享资源:多个进程或者线程都可以操作的资源称为共享资源。对共享资源的操作代码段称为临界区。
  • 影响 :对共享资源的无序操作可能会带来数据的混乱,或者操作错误。此时往往需要同步互斥机制协调操作顺序。

3. 同步互斥机制

同步 : 同步是一种协作关系,为完成操作,多进程或者线程间形成一种协调,按照必要的步骤有序执行操作。

互斥 : 互斥是一种制约关系,当一个进程或者线程占有资源时会进行加锁处理,此时其他进程线程就无法操作该资源,直到解锁后才能操作。

线程同步互斥方法

线程Event

from threading import Event

e = Event()创建线程event对象

e.wait([timeout])阻塞等待e被set

e.set()设置e,使wait结束阻塞

e.clear() 使e回到未被设置状态

e.is_set() 查看当前e是否被设置

from threading import Thread,Event

s = None  # 用于通信
e = Event() # 创建event对象

def 杨子荣():
  print("杨子荣前来拜山头")
  global s
  s = "天王盖地虎"
  e.set()  # 对e设置

t = Thread(target=杨子荣)
t.start()

print("说对口令就是自己人")
e.wait()  # 阻塞等待口令说出
if s == '天王盖地虎':
  print("宝塔镇河妖")
  print("确认过眼神,你是对的人")
else:
  print("打死他...")


t.join()

线程锁 Lock

from threading import Lock

lock = Lock() 创建锁对象

lock.acquire() 上锁 如果lock已经上锁再调用会阻塞

lock.release() 解锁

with lock:# 上锁

...

...

with代码块结束自动解锁

from threading import Thread,Lock

a = b = 0
lock = Lock() # 定义锁

def value():
  while True:
    lock.acquire()  # 上锁
    if a != b:
      print("a = %d,b = %d"%(a,b))
    lock.release() # 解锁

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

while True:   # 上锁
  with lock:
    a += 1
    b += 1
              # 自动解锁
t.join()

python线程的GIL问题

GIL (全局解释器锁)

python ---》 支持线程操作 ---》IO的同步和互斥 --》 加锁 ----》 超级锁,给解释器加锁

后果:一个解释器,同一时刻只解释一个线程,此时其他线程需要等待。大大降低了python线程的执行效率

python GIL问题解决方案

* 修改c解释器

* 尽量使用多进程进行并行操作

* python线程可以用在高延迟多阻塞的IO情形

* 不使用cpython  c# java做解释器

效率测试

分别测试 多进程 多线程 单进程执行相同的IO操作和CPU


#计算密集
def count(x,y):
    c = 0
    while c < 7000000:
        x += 1
        y += 1
        c += 1

#io密集
def write():
    f = open("test.txt",'w')
    for x in range(2000000):
        f.write("hello world\n")
    f.close()

def read():
    f = open("test.txt")
    lines = f.readlines()
    f.close()

View Code

操作的时间


#单进程程序
from test import *
import time 

# t = time.time()
# for i in range(10):
#     count(1,1)
# print("Line cpu:",time.time() - t)


t = time.time()
for i in range(10):
    write()
    read()
print("Line IO:",time.time() - t)

View Code

Line cpu: 8.15166711807251

Line IO: 6.841825246810913


from test import * 
import threading 
import time 

counts = []

t = time.time()

for x in range(10):
    th = threading.Thread(target = count,args = (1,1))
    th.start()
    counts.append(th)

for i in counts:
    i.join()
print("Thread cpu",time.time() - t)

View Code

from test import * 
import threading 
import time 

counts = []

def io():
    write()
    read()

t = time.time()

for x in range(10):
    th = threading.Thread(target = io)
    th.start()
    counts.append(th)

for i in counts:
    i.join()
print("Thread IO",time.time() - t)

View Code

Thread cpu 8.414522647857666

Thread IO 6.023292541503906


from test import * 
import multiprocessing 
import time 

counts = []

t = time.time()

for x in range(10):
    th = multiprocessing.Process\
    (target = count,args = (1,1))
    th.start()
    counts.append(th)

for i in counts:
    i.join()
print("Process cpu",time.time() - t)

View Code

from test import * 
import multiprocessing 
import time 

counts = []

def io():
    write()
    read()

t = time.time()

for x in range(10):
    th = multiprocessing.Process(target = io)
    th.start()
    counts.append(th)

for i in counts:
    i.join()
print("Process IO",time.time() - t)

View Code

Process cpu 4.079084157943726

Process IO 3.2132551670074463

进程和线程的区别和联系

  1. 两者都是多任务编程的方式,都能够使用计算机的多核
  2. 进程的创建删除要比线程消耗更多的计算机资源
  3. 进程空间独立,数据安全性好,有专门的进程间通信方法
  4. 线程使用全局变量通信,更加简单,但是需要同步互斥操 作
  5.  一个进程可以包含多个线程,线程共享进程的空间资源
  6.  进程线程都独立执行,有自己的特有资源如属性,id, 命令集等

使用情况:

  • 一个进程中并发任务比较多,比较简单,适合使用多线程
  • 如果数据程序比较复杂,特别是可能多个任务通信比较多 的时候,要考虑到使用线程同步互斥的复杂性
  • 多个任务存在明显差异,和功能分离的时候没有必要一定 写入到一个进程中
  • 使用python考虑线程GIL问题
我来评几句
登录后评论

已发表评论数()

相关站点

热门文章