-
什么是线程?
程序的执行线路。每个进程默认有一条线程。线程包含了程序的具体步骤。
多线程就是一个进程中有除主线程(默认线程)外还有多个线程。
-
线程与进程的关系(进程包含线程,而线程依赖进程存在)
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() -
进程和线程的对比
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 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 -
守护线程
无论是进程还是线程:都遵循守护一方等待主方运行完毕后被销毁。运行完毕不是终止运行。
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 -
信号量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() -
生产者消费者模型中的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() # 明确生产方已经不会再生成数据了
python中线程的知识点的更多相关文章
-
python中线程和进程(一)
目录 进程和线程 Python中的线程 1. Thread类 2. 线程的启动 3. 线程的传参 4. 线程的属性和方法 5. daemon线程和non-daemon线程 6. join方法 7. 定 ...
-
操作系统/应用程序、操作中的“并发”、线程和进程,python中线程和进程(GIL锁),python线程编写+锁
并发编程前言: 1.网络应用 1)爬虫 直接应用并发编程: 2)网络框架 django flask tornado 源码-并发编程 3)socketserver 源码-并发编程 2.运维领域 1)自动 ...
-
python中线程和进程的简单了解
python中线程和进程的简单了解 一.操作系统.应用程序 1.硬件:硬盘.cpu.主板.显卡........ 2.装系统(本身也是一个软件): 系统就是一个由程序员写出来的软件,该软件用于控制计 ...
-
python中的小知识点
这里是一些小知识点的汇集,为的是以后查找的方便. 行与缩进: 物理行:实际看到的代码行数. 逻辑行:在意义上的函数(即解释器执行的行数) 如果一个物理行中包含了多个逻辑行,则每个逻辑行之间需要用分号 ...
-
python中线程2
cpython中的GIL和pool GIL锁(全局解释器锁) 1.what? GIL是全局解释器锁,和普通锁加在数据上不同的是:GIL加在加在解释器上,是为了防止多个线程在同一时间执行python字节 ...
-
Python中线程的使用
并发:多个任务同一时间段进行 并行:多个任务同一时刻进行 线程的实现 线程模块 Python通过两个标准库_thread 和threading,提供对线程的支持 , threading对_thread ...
-
python中线程和进程(二)
目录 线程同步 Event Lock RLock Condition Barrier semaphore GIL 线程同步 线程同步,即线程之间协同工作,一个线程访问某些数据时,其他线程不能访问这些数 ...
-
Python中线程与互斥锁
了解之前我们先了解一下什么是多任务? 概念: 几个不同的事件在同时运行就是多任务, 这样的话, 我们有牵扯到了真的多任务, 假的多任务; 并行: 真的多任务, 通过电脑的核数来确定 并发: 假的多任务 ...
-
python中线程、进程和协程的区别
进程是资源分配的单位 线程是操作系统调度的单位 协程,又称微线程,纤程,协程的切换只是单纯的操作CPU的上下文,资源很小,效率高 进程切换需要的资源很最大,效率很低 一个程序至少有一个进程,一个进程至 ...
随机推荐
-
ASP.NET MVC5+EF6+EasyUI 后台管理系统(63)-Excel导入和导出-自定义表模导入
系列目录 前言 上一节使用了LinqToExcel和CloseXML对Excel表进行导入和导出的简单操作,大家可以跳转到上一节查看: ASP.NET MVC5+EF6+EasyUI 后台管理系统(6 ...
-
RMAN还原遭遇ORA-32006&;ORA-27102错误
案例环境: 服务器A: 操作系统 : Red Hat Enterprise Linux ES release 4 (Nahant Update 6) 数据库版本: Oracle Database ...
-
java-原生爬虫机制源码
这是一个web搜索的基本程序,从命令行输入搜索条件(起始的URL.处理url的最大数.要搜索的字符串),它就会逐个对Internet上的URL进行实时搜索,查找并输出匹配搜索条件的页面. 这个程序的原 ...
-
VisualSvn server 权限配置
库上,配置 EveryOne 有读写权限. 下面的文件夹,再根据情况,取消 EveryOne 的读写权限,添加另一个用户组的读写权限. 它的规则是: 子目录权限覆盖父目录权限.
-
Arch yaourt 安装
安装yaourt,最简单安装Yaourt的方式是添加Yaourt源至您的 /etc/pacman.conf:[archlinuxcn]#The Chinese Arch Linux communiti ...
-
Android圆形图片--ImageView
[ RoundImageView.java ] package com.dxd.roundimageview; import android.content.Context; import andro ...
-
前端笔记——获取url里面的参数值
备注 var url=window.location.href;//获取地址栏 url var index=url.indexOf('#');//获取#的位置 var paramVal=url.sub ...
-
从零开始学android开发-项目debug
在红框处能看到变量值
-
VS2010 IE10 调试时报“未能将脚本调试器附加到计算机”,已经附加了一个进程
解决办法:以管理员身份打开CMD,运行:regsvr32.exe "%ProgramFiles(x86)%\Common Files\Microsoft Shared\VS7Debug\ms ...
-
android官网文档学习笔记
1.android的四大组件的了大概功能 activity:负责显示界面,和用户交互. service:运行在后台. content provider:为程序app之间的数据访问提供接口. broad ...