前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >并发编程(三)

并发编程(三)

作者头像
HammerZe
发布2022-03-24 15:33:25
1940
发布2022-03-24 15:33:25
举报
文章被收录于专栏:Hammer随笔

目录

线程

查看源图像
查看源图像

简介

线程是一个资源单位,真正被CPU执行的其实是进程里面的线程,例如打开word是打开一个进程,那么在里执行写,读,插入图片等操作就是进行一个个线程操作; 通俗理解:进程类似于工厂,线程类似于是工厂里面的一条条流水线,所有的进程肯定是最少有一个线程; 进程间数据默认是隔离的,但是同一个进程内的多个线程数据是共享的;

image
image

开设线程的两种方式

  • 开设进程需要做哪些操作

重新申请一块内存空间 将所需的资源全部导入

  • 开设线程需要做哪些操作

上述两个步骤都不需要 所以开设线程消耗的资源远比开设进程的少

方法一:

代码语言:javascript
复制
from threading import Thread
import time


def test(name):
    print(f'{name}is running!')
    time.sleep(2)
    print(f'{name}is over!')

# 开设线程可以不用在main判断语句下开设
t = Thread(target=test,args=('Hammer',))
t.start()

print('主进程')  


# 线程不需要考虑内存空间和资源的问题,也不需要等待主进程执行完再执行子进程等问题
# 所以开设线程所需要的时间更短,速度更快

方法二:

代码语言:javascript
复制
# 和进程类似,继承的方式
class Myclass(Thread):
    def __init__(self,name):
        super().__init__()
        self.name = name

    def run(self):
        print(f'{self.name} is running !')
        time.sleep(2)
        print(f'{self.name} is over !')
      
t = Myclass('Hammer')
t.start()

线程对象的join方法

在进程中的join方法是,主进程等待子进程执行完再运行; 线程中顾名思义是一样的,主线程等待子线程执行完再执行

代码语言:javascript
复制
from threading import Thread
import time

def test(name):
    print(f'{name} is running!')
    time.sleep(2)
    print(f'{name} is over! ')


# 开设线程
t = Thread(target=test,args=('子线程',))
t.start()
t.join()
print('主线程')

通过os.getpid()方法获取进程号来验证两个线程同属于一个进程

代码语言:javascript
复制
from threading import Thread
import time
import os
def test(name):
    print(os.getpid()) # 9436
    print(f'{name} is running!')
    time.sleep(2)
    print(f'{name} is over! ')


# 开设线程
t = Thread(target=test,args=('子线程',))
t.start() # 9436
print(os.getpid())
print('主线程')

线程之active_count模块

统计当前活跃的线程数

代码语言:javascript
复制
from threading import Thread,active_count
import time

def test(name):
    print(f'{name} is running !')
    time.sleep(2)
    print(f'{name} is over !')

t = Thread(target=test,args=('线程1',))
t1 = Thread(target=test,args=('线程2',))
t.start()
t1.start()
print(active_count())  # 3 
# 现有活跃线程数为3的原因是主线程加2个子线程

线程之current_thread模块

获取当前线程的名字

代码语言:javascript
复制
from threading import Thread,current_thread
import  time


def test(name):
    print(f'子线程名:>>>{current_thread().name}')
    print(f'{name} is running !')
    time.sleep(2)
    print(f'{name} is over!')


t = Thread(target=test,args=('线程1',))
t1 = Thread(target=test,args=('线程2',))
t.start()
t1.start()
print(f'主线程名:>>> {current_thread().name}')

# 结果
子线程名:>>>Thread-1
线程1 is running !
子线程名:>>>Thread-2
线程2 is running !
主线程名:>>> MainThread
线程2 is over!
线程1 is over!



# 子线程可以通过t.name直接拿,主线程必须通过方法.name拿

守护线程

类似守护进程一样,一个线程结束其他线程也得结束(陪葬) 主线程的结束意味着整个进程的结束,所以主线程需要等待里面所有非守护线程的结束才能结束

代码语言:javascript
复制
from threading import Thread
import time

def test(name):
    print(f'{name} is running ')
    time.sleep(2)
    print(f'{name} is over !')

t = Thread(target=test,args=('子线程',))
t.daemon = True  # 守护线程必须放在start上
t.start()

print('主线程')

迷惑的例子,判断打印顺序

代码语言:javascript
复制
from threading import Thread
from multiprocessing import Process
import time
def foo():
    print(123)
    time.sleep(3)
    print("end123")
def bar():
    print(456)
    time.sleep(1)
    print("end456")
if __name__ == '__main__':
    t1=Thread(target=foo)
    t2=Thread(target=bar)
    t1.daemon=True
    t1.start()
    t2.start()
    print("main-------")
    
    
# 结果
123
456
main-------
end123
end456

# 结果分析
开设线程直接打印123,然后遇到sleep方法
打印456,遇到sleep方法
打印main
这时候主线程没有直接结束,等待非守护线程(bar)结束才能结束,然而bar()方法中,sleep睡了3秒,那么print('end123')睡1秒就打印了

线程数据共享

验证同一个进程内线程间数据共享 注意是同一进程下!

代码语言:javascript
复制
from threading import Thread
money = 100

def test():
    global money
    money = 999

t = Thread(target=test)
t.start()
t.join()
print(money)

# 结果
999
# 这样是修改了的,和进程不一样

线程互斥锁

多个线程修改数据和多进程修改数据一样容易出现数据错乱,这样需要线程互斥锁来解决这个问题; 对比进程互斥锁中的查票买票代码理解;

代码语言:javascript
复制
from threading import Thread, Lock
from multiprocessing import Lock
import time


num = 100


def test(mutex):
    global num
    mutex.acquire()   # 抢锁
    # 先获取num的数值
    tmp = num
    # 模拟延迟效果
    time.sleep(0.1)
    # 修改数值
    tmp -= 1
    num = tmp
    mutex.release()   # 释放锁

t_list = []

# 声明锁
mutex = Lock()

for i in range(100):
    t = Thread(target=test, args=(mutex,))
    t.start()
    t_list.append(t)
# 确保所有的子线程全部结束
for t in t_list:
    t.join()
print(num)

# 如果不加锁,那么输出的num是99,比如减100次应该是0,在这里并发操作不加锁会产生数据的错乱,需要变成串行

补:TCP服务端实现并发

服务端

代码语言:javascript
复制
import socket
from threading import Thread
from multiprocessing import Process

server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)

# 和用户交互的代码封装
def talk(sock):
    while True:
        try:
            data = sock.recv(1024)
            if len(data) == 0: break
            print(data.decode('utf8'))
            sock.send(data + b'Hi!')
        except ConnectionResetError as e:
            print(e)
            break
    sock.close()


while True:
    sock, addr = server.accept()
    print(addr)
    # 开设多进程或者多线程
    t = Thread(target=talk, args=(sock,))
    t.start()

客户端

代码语言:javascript
复制
import socket

client = socket.socket()
client.connect(('127.0.0.1', 8080))

whlie True:
    client.send(b'hello')
    data = client.recv(1024)
    print(data.decode('utf8'))
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-01-19 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 线程
    • 简介
      • 开设线程的两种方式
        • 方法一:
        • 方法二:
      • 线程对象的join方法
        • 线程之active_count模块
          • 线程之current_thread模块
            • 守护线程
              • 线程数据共享
                • 线程互斥锁
                  • 补:TCP服务端实现并发
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档