python中网络编程之线程
网络编程之线程
什么是线程?
程序的执行线路。每个进程默认有一条线程。线程包含了程序的具体步骤。
多线程就是一个进程中有除主线程(默认线程)外还有多个线程。
线程与进程的关系(进程包含线程,而线程依赖进程存在)
1.进程包含了运行该程序的所有资源,是一个资源单位。
2.线程是CPU的执行单位(最小的执行单位)。
3.进程一旦被创建,就默认开启了一条线程,称之为主线程。
4.线程共享创建它的进程的地址空间;进程有自己的地址空间。
5.线程可以直接访问其进程的数据段;进程有它们自己的父进程的数据段副本。
6.线程可以直接与进程的其他线程通信;进程必须使用进程间通信来与兄弟进程通信。
7.容易创建新线程;新进程需要父进程的复制。
8.线程可以对同一进程的线程进行相当大的控制;流程只能对子流程进行控制。
9.对主线程的更改(取消、优先级更改等)可能会影响进程中其他线程的行为;对父进程的更改不会影响子进程。
为什么使用多线程?
为了提高程序运行效率。与进程区别是线程对于系统资源的占用非常小。
1.多个线程共享一个进程的地址空间。
2.线程比进程对系统资源占用小,创建速度快10-100倍。
3.同一个进程中多个线程之间资源共享,不需要像进程一样需要进程间通信。
线程的两种方式
- #第一种方式:导入threading模块中的Thread类来创建一个对象
- from threading import Thread
-
- def task():
- print('子线程 running。。。')
- t = Thread(target=task)
- t.start()
- print('over')
- # from threading import Thread
- #
- # def task():
- # print('子线程 running。。。')
- #
- # if __name__ == '__main__':
- # t = Thread(target=task)
- # t.start()
- # print('over')
-
-
- #第二种方式:创建一个子类继承Thread类,可自定义run方法,但是不能改变run方法名。
- from threading import Thread
- class MyThread(Thread):
- def __init__(self, name):
- super().__init__()
- self.name = name
- def run(self):
- print('%s say hi' % self.name)
- if __name__ == '__main__':
- t = MyThread('daidai')
- t.start()
- #第一种方式:导入threading模块中的Thread类来创建一个对象
进程和线程的对比
- from multiprocessing import Process
- from threading import Thread
- import time
-
- def task():
- pass
- #开启100个线程花费的时间
- start_time = time.time()
- ts = []
- for i in range(100):
- t = Thread(target=task)
- t.start()
- ts.append(t)
- print(os.getpid()) # 所有的进程编号都一样,同属一个进程中
- for t in ts:
- t.join()
- print(time.time()-start_time)
- ###
- 0.018949270248413086
-
- #开启100个进程花费的时间
- if __name__ == '__main__':
- start = time.time()
- ps = []
- for i in range(100):
- p = Process(target=task)
- p.start()
- ps.append(p)
- for p in ps:
- p.join()
- print(time.time()-start)
- ###
- 5.281934499740601
- from multiprocessing import Process
线程间的资源共享
- from threading import Thread
- x = 100
- def task():
- print("run....")
- global x # 修改全局变量
- x = 0
- t = Thread(target=task)
- t.start()
- t.join() # join方法将子线程的优先级提高到主线程前
- print(x)
- print("over")
- ####
- run....
- 0
- over
- from threading import Thread
守护线程
无论是进程还是线程:都遵循守护一方等待主方运行完毕后被销毁。运行完毕不是终止运行。
1.对于主进程来说,运行完毕是主代码运行完毕。
2.对于主线程来说,运行完毕是主线程内所有非守护线程运行完毕。所以守护线程会在所有非守护线程结束后结束。
详细解释:
#1.主进程在其代码结束后就已经算运行完毕(守护进程在此时会被回收),然后著京城会一直等着非守护的子进程都运行完毕后回收子进程的资源(否则就会产生僵尸进程),才会结束。
#2.主线程在其他非守护线程运行完毕后才算运行完毕(守护线程在此时就会被回收)。因为主线程的结束意味着进程的结束,进程整体的资源都将被回收,而进程必须保证非守护线程都运行完毕后才能结束。
-
- from threading import Thread
- import time
-
- def task():
- print('sub thread running')
- time.sleep(3)
- print('sub thread over')
-
- t = Thread(target=task)
- t.setDaemon(True) # 要在开启之前设置
- t.start()
- print('main thread over')
- ###
- sub thread running
- main thread over
Thread类对象常用的属性和方法
对象的方法:
isAlive():返回线程是否活动
getName():返回线程名
setName():设置线程名
threading模块的一些方法:
current_thread():返回当前线程变量,获取当前线程
enumerate():返回一个包含正在运行线程的列表
active_count():返回正在进行的线程数量,与len(threading.enumerate())有相同的结果。
- from threading import Thread,current_thread,enumerate,active_count
- import time
- import os
- def task():
- time.sleep(3)
- print(current_thread().getName())
- t = Thread(target = task)
- t.start()
- print(current_thread().getName())
- print(current_thread())
- print(enumerate())
- print(active_count())
- print('daidai')
- #运行结果
- MainThread
- <_MainThread(MainThread, started 14652)>
- [<_MainThread(MainThread, started 14652)>, <Thread(Thread-1, started 15548)>]
- 2
- daidai
- Thread-1
-
-
- t.join() #主线程等待子线程结束
-
- 线程互斥锁
- 当多个进程或者多个线程需要同时修改同一份数据时,可能造成数据的错乱,所以需要给说句加上锁。
- 同样的线程中也有死锁和可重入锁RLock
- import time
- from threading import Thread, Lock
- lock = Lock()
- a = 100
- def task():
- lock.acquire()
- global a # 修改全局变量a
- temp = a-1
- time.sleep(0.05)
- a = temp
- lock.release()
- ts = []
- for i in range(100):
- t = Thread(target=task)
- t.start()
- ts.append(t)
-
- for t in ts:
- t.join()
-
- print(a) # 全局中的a已经被修改为0
- from threading import Thread,current_thread,enumerate,active_count
信号量Semaphore
semaphore管理一个内置的计数器,每当调用acquire()时内置计数器-1:
调用release()时内置计数器+1;
计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。
信号量其实也是一种锁,特点是可以设置一个数据可以被几个线程(进程)共享。
与普通锁的区别:
普通锁一旦加锁就意味着这个数据在同一时间只能被一个线程或进程使用。
信号量可以让数据在同一时间能被多个线程使用。
- #实例:开启10个线程,每次运行3个
- from threading import Semaphore, Thread, current_thread
- import time
-
- sem = Semaphore(3)
- def task():
- sem.acquire()
- print('%s task running' % current_thread())
- time.sleep(3)
- sem.release()
-
- for i in range(10):
- t = Thread(target=task)
- t.start()
- #实例:开启10个线程,每次运行3个
生产者消费者模型中的JoinableQueue
JoinableQueue类是一种队列,Queue的子类。但是实例化的对象可以明确直到队列中是否有数据及数据的使用量。
参数介绍:
maxsize是队列中允许最大项数,省略则无大小限制。
方法介绍:
JoinableQueue的实例除了与Queue实例对象相同方法之外还有:
task_done():使用者使用此方法发出信号,表示q.get()的返回项目已经被处理。如果调用此方法的次数大于从队列中删除项目的数量,将引发异常。
q.join():明确生产者不会再生产数据。加入数据到队列中。
- import time
- import random
- from multiprocessing import Process, JoinableQueue
- def eat_hotdog(name, q):
- while True:
- res = q.get()
- if not res:
- print('吃完了。。。')
- break
- print('%s吃了%s' % (name, res))
- time.sleep(random.randint(1, 2))
- q.task_done() #向q.join()发送一次信号,证明一个数据已经被取走了
- def make_hotdog(name, q):
- for i in range(1,6):
- time.sleep(random.randint(1, 2))
- print('%s 生产了第%s个热狗' % (name,i))
- res = '%s的%s个热狗'% (name,i)
- q.put(res)
-
- if __name__ == '__main__':
- q = JoinableQueue()
- #生产者1
- c1 = Process(target=make_hotdog, args=('万达', q))
- c1.start()
- #生产者2
- c2 = Process(target=make_hotdog, args=('呆呆', q))
- c2.start()
-
- p2 = Process(target=eat_hotdog, args=('思聪', q))
- p2.daemon = True
- p2.start()
- # 首先保证生产者全部产完成
- c1.join()
- c2.join()
- # 保证队列中的数据全部被处理了
- q.join() # 明确生产方已经不会再生成数据了
- import time
python中网络编程之线程的更多相关文章
- python中网络编程基础
一:什么是c\s架构 1.c\s即client\server 客户端\服务端架构. 客户端因特定的请求而联系服务器并发送必要的数据等待服务器的回应最后完成请求 服务端:存在的意义就是等待客户端的请求, ...
- python中网络编程
网络编程软件架构介绍: C/S:客户端,服务端 B/S:浏览器,服务端 # 常见应用: 1.手机端看着感觉是c/s架构其实更多的是b/s架构,例如微信小程序,支付宝第三方接口 2.pc端:b/s比较火 ...
- Python中网络编程对 listen 函数的理解
listen函数的第一个参数时SOCKET类型的,该函数的作用是在这个SOCKET句柄上建立监听,至于有没有客户端连接进来,就需要accept函数去进行检查了,accept函数的第一个参数也是SOCK ...
- Python中网络编程对socket accept函数的理解
在服务器端,socket()返回的套接字用于监听(listen)和接受(accept),这个套接字不能用于与客户端之间发送和接收数据. accept()接受一个客户端的连接请求,并返回一个新的套接字, ...
- Python高级网络编程系列之第一篇
在上一篇中我们简单的说了一下Python中网络编程的基础知识(相关API就不解释了),其中还有什么细节的知识点没有进行说明,如什么是TCP/IP协议有几种状态,什么是TCP三次握手,什么是TCP四次握 ...
- python网络编程基础(线程与进程、并行与并发、同步与异步、阻塞与非阻塞、CPU密集型与IO密集型)
python网络编程基础(线程与进程.并行与并发.同步与异步.阻塞与非阻塞.CPU密集型与IO密集型) 目录 线程与进程 并行与并发 同步与异步 阻塞与非阻塞 CPU密集型与IO密集型 线程与进程 进 ...
- python之网络编程
本地的进程间通信(IPC)有很多种方式,但可以总结为下面4类: 消息传递(管道.FIFO.消息队列) 同步(互斥量.条件变量.读写锁.文件和写记录锁.信号量) 共享内存(匿名的和具名的) 远程过程调用 ...
- python中的进程、线程(threading、multiprocessing、Queue、subprocess)
Python中的进程与线程 学习知识,我们不但要知其然,还是知其所以然.你做到了你就比别人NB. 我们先了解一下什么是进程和线程. 进程与线程的历史 我们都知道计算机是由硬件和软件组成的.硬件中的CP ...
- python基础网络编程--转
python之网络编程 本地的进程间通信(IPC)有很多种方式,但可以总结为下面4类: 消息传递(管道.FIFO.消息队列) 同步(互斥量.条件变量.读写锁.文件和写记录锁.信号量) 共享内存(匿名的 ...
随机推荐
- 第六节《Git克隆》
本节学习如何使用git clone命令建立版本库克隆,以及如何使用git push和gitpull命令实现克隆之间的同步. Git的版本库目录和工作区在一起,因此存在一损俱损的问题,即如果删除一个项目 ...
- docker-compose up使用自定义的网段的两种方式(从其根源指定)
问题描述 还是那个研究安全大业的同事,在部署他的秘密武器,是用docker-compose部署的,有差不多20多个docker-compose.yml文件,然后由于docker-com ...
- Cent os6.5 安装python3.2
1.CentOS6.5 安装Python 的依赖包 yum groupinstall "Development tools" yum install zlib-devel bzip ...
- OpenStack搭建Q版在控制节点上的环境准备(step2)
接下来是只需要在控制节点上准备的环境配置.其中虽然NTP服务需要在所有节点上都安装,但NTP服务在控制节点和其他的节点上的配置是不同的,所以不把它放在step1的公共配置中进行准备.如下: 1.配置N ...
- .NET MVC 表主外键关系 JSON 无限循环 方案二(推荐)
public class JsonResultObject:JsonResult { private Newtonsoft.Json.JsonSerializerSettings Settings { ...
- HashTable & HashMap & ConcurrentHashMap 原理与区别
一.三者的区别 HashTable HashMap ConcurrentHashMap 底层数据结构 数组+链表 数组+链表 数组+链表 key可为空 否 是 否 value可为空 否 是 否 ...
- 转 一个oracle11g 使用exp导出空表丢失的问题分析及解决办法
用exp无法导出空表解决方法 最早的一次使用oracle 11g导出数据发现有的表丢失了,感觉莫名其妙的,后来终于找到原因了. 找到问题以后,再看看解决方案.11GR2中有个新特性,当表无数据时,不分 ...
- PLSQL脚本中自定义异常的简单使用
第一步:自定义异常 excp_notexists exception 第二步:捕获异常 begin select *** into *** from *** where ...
- 小程序中添加客服按钮contact-button
小程序的客服系统,是微信做的非常成功的一个功能,开发者可以很方便的通过一行代码,就可实现客服功能. 1. 普通客服按钮添加 <button open-type='contact' session ...
- 未来-区块链-IBM:IBM 区块链技术开发社区
ylbtech-未来-区块链-IBM:IBM 区块链技术开发社区 1.返回顶部 1. 开始学习 IBM Blockchain 101:开发人员快速入门指南 这篇快速入门指南适合不熟悉区块链技术,希望快 ...