热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

python线程安全的计数器_python多线程的原理和实现

申明下哈本篇文章不是自己写的根据网上的文章再加上自己的加加点点反正大部分都是网站的智慧哈!!!1、线程基本概念1.1线程是什么࿱

申明下哈 本篇文章不是自己写的 根据网上的文章再加上自己的加加点点 反正大部分都是网站的智慧哈!!!

1、线程基本概念

1.1 线程是什么?

线程是指进程内的一个执行单元,也是进程内的可调度实体.

与进程的区别:

(1) 地址空间:进程内的一个执行单元;进程至少有一个线程;它们共享进程的地址空间;而进程有自己独立的地址空间;

(2) 资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源

(3) 线程是处理器调度的基本单位,但进程不是.

(4) 二者均可并发执行.

简而言之,一个程序至少有一个进程,一个进程至少有一个线程.

线程的划分尺度小于进程,使得多线程程序的并发性高。

另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。

1.2 线程和进程关系?

​ 进程就是一个应用程序在处理机上的一次执行过程,它是一个动态的概念,而线程是进程中的一部分,进程包含多个线程在运行。

​ 多线程可以共享全局变量,多进程不能。多线程中,所有子线程的进程号相同;多进程中,不同的子进程进程号不同。

​ 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.

​ 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.

​ 一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.

2、Python线程模块

​ python主要是通过thread和threading这两个模块来实现多线程支持。python的thread模块是比较底层的模块,python的threading模块是对thread做了一些封装,可以更加方便的被使用。但是python(cpython)由于GIL的存在无法使用threading充分利用CPU资源,如果想充分发挥多核CPU的计算能力需要使用multiprocessing模块(Windows下使用会有诸多问题)。

2.1 如何创建线程

我自己不懂python2 所以直接拿python3来写。。。

python3.x中通过threading模块创建新的线程有两种方法:

(1)第一种是通过threading.Thread(Target=executable Method)-即传递给Thread对象一个可执行方法(或对象)

(2)第二种是继承threading.Thread定义子类并重写run()方法。第二种方法中,唯一必须重写的方法是run()

实例:通过threading.Thread进行创建多线程

import threading

import time

def target():

print("当前线程名为:%s 正在运行中"

% (threading.current_thread().name))

time.sleep(1)

print("当前线程名为:%s 正在运行中" %

(threading.current_thread().name))

print("当前线程名为:%s 正在运行中" % (threading.current_thread().name))

# 属于线程t的部分

t = threading.Thread(target=target)

t.start()

# 属于线程t的部分

t.join() # join是阻塞当前线程(此处的当前线程时主线程) 主线程直到Thread-1结束之后才结束

print("当前线程名为:%s 已经结束" % (threading.current_thread().name))

此时运行的线程一共有两个,一个主线程一个子线程!!!

其实我们在这里还可以探讨下为什么最后是MianThread最后结束的呢?

答:原因是因为有join函数

join函数的作用是:当遇到join的时候使主线程阻塞,等子线程运行完成后再继续主线程,这样我们就可以获得两个线程并行运行的时间了。只需要对子线程调用join方法就可以将主线程阻塞

那么我们有没有办法使得主线程退出后子线程自动退出呢(主线程不需要等子线程完成后再继续往下走)?

答:通过对子线程调用setDaemon方法,将子线程标记成守护线程

实例:

# coding=utf-8

import threading

import time

def to_f(time_time_time):

time.sleep(2)

print('子线程%s完成了哈!!完成时间->%s' %

(threading.current_thread().name, time_time_time))

for i in range(2):

t = threading.Thread(target=to_f, args=(time.time(),))

t.setDaemon(1)

t.start()

print('主线程完成!!!')

这里的话不需要等待子线程了 直接完成 由于主线程运行到结尾的时候 子线程还在sleep 所以直接结束子线程!!!

再看这个:通过继承threading.Thread定义子类创建多线程

使用Threading模块创建线程,直接从threading.Thread继承,然后重写init方法和run方法:

import threading

import time

class myThread(threading.Thread): # 继承父类threading.Thread

def __init__(self, threadID, name, counter):

threading.Thread.__init__(self)

self.threadID = threadID

self.name = name

self.counter = counter

def run(self): # 把要执行的代码写到run函数里面 线程在创建后会直接运行run函数

print("Starting " + self.name)

print_time(self.name, self.counter, 5)

print("Exiting " + self.name)

def print_time(threadName, delay, counter):

while counter:

time.sleep(delay)

print("%s process at: %s" % (threadName, time.ctime(time.time())))

counter -= 1

# 创建新线程

thread1 = myThread(1, "Thread-1", 1)

thread2 = myThread(2, "Thread-2", 2)

# 开启线程

thread1.start()

thread2.start()

# 等待线程结束

thread1.join()

thread2.join()

print("Exiting Main Thread")

结果:

结果发现子线程-1 子线程-2 不是有序的进行执行 而是看来像抢着执行的 但是由于thread-1 start()先开始的 所以thread-1优先结束的可能性大哈!!! 我们也知道多线程之间的变量是共享的 如果如上方的情况发生那数据岂不是会直接混乱起来 那么有啥方法呢?

3、线程间同步

如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步。

​ 使用Thread对象的Lock和Rlock可以实现简单的线程同步,这两个对象都有acquire方法和release方法,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到acquire和release方法之间。

​ 需要注意的是,Python有一个GIL(Global Interpreter Lock)机制,任何线程在运行之前必须获取这个全局锁才能执行,每当执行完100条字节码,全局锁才会释放,切换到其他线程执行。

3.1 线程同步问题

多线程实现同步有四种方式:

锁机制,信号量,条件判断和同步队列。

下面我主要关注两种同步机制:锁机制和同步队列。

(1)锁机制

threading的Lock类,用该类的acquire函数进行加锁,用realease函数进行解锁

threadLock = threading.Lock() # threading的Lock类来获得一个锁

代码如下:

import threading

import time

class myThread(threading.Thread): # 继承父类threading.Thread

def __init__(self, threadID, name, sleep_time):

threading.Thread.__init__(self)

self.threadID = threadID

self.name = name

self.sleep_time = sleep_time

def run(self): # 把要执行的代码写到run函数里面 线程在创建后会直接运行run函数

print("Starting " + self.name)

threadLock.acquire()

print_time(self.name, self.sleep_time, 5)

print("Exiting " + self.name)

threadLock.release()

def print_time(threadName, delay, counter):

while counter:

time.sleep(delay)

print("%s process at: %s" % (threadName, time.ctime(time.time())))

counter -= 1

threadLock = threading.Lock()

# 创建新线程

thread1 = myThread(1, "Thread-1", 1)

thread2 = myThread(2, "Thread-2", 2)

# 开启线程

thread1.start()

thread2.start()

# 等待线程结束

thread1.join()

thread2.join()

print("Exiting Main Thread")

这里对于锁的理解:

假如有两个线程A和B,A和B里的程序都加了同一个锁对象,当线程A率先执行到lock.acquire() (ps.拿到全局唯一的锁后).

线程B只能等到线程A释放锁lock.release()后(归还锁)才能运行lock.acquire()(拿到全局唯一的锁)并执行后面的代码

其中锁也可以这样实现

lock = threading.Lock()

with lock: # with语句会在这个代码块执行前自动获取锁,在执行结束后自动释放锁

# 这里写自己的代码

pass

关于锁的内容还可以参考https://www.jianshu.com/p/9fa218e50a16

(2) 线程同步队列queue

用法:import queue.

Python的queue模块中提供了同步的、线程安全的队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列PriorityQueue。这些队列都实现了锁原语,能够在多线程中直接使用。可以使用队列来实现线程间的同步。

queue模块中的常用方法:

queue.qsize() 返回队列的大小

queue.empty() 如果队列为空,返回True,反之False

queue.full() 如果队列满了,返回True,反之False

queue.full 与 maxsize 大小对应

queue.get([block[, timeout]])获取队列,timeout等待时间

queue.get_nowait() 相当Queue.get(False)

queue.put(item) 写入队列,timeout等待时间

queue.put_nowait(item) 相当Queue.put(item, False)

queue.task_done() 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号

queue.join() 实际上意味着等到队列为空,再执行别的操作

实例1

import queue

import threading

import time

exitFlag = 0

class myThread(threading.Thread):

def __init__(self, threadID, name, q):

threading.Thread.__init__(self)

self.threadID = threadID

self.name = name

self.q = q

def run(self):

print("Starting " + self.name)

process_data(self.name, self.q)

print("Exiting " + self.name)

def process_data(threadName, q):

while not exitFlag:

queueLock.acquire()

if not workQueue.empty():

data = q.get()

queueLock.release()

print("%s processing %s" % (threadName, data))

else:

queueLock.release()

time.sleep(1)

queueLock = threading.Lock()

workQueue = queue.Queue(100)

threads = []

threadID = 1

# 创建新线程

for tName in range(10): # 创建10个线程

thread = myThread(threadID, '线程-' + str(tName) + '号', workQueue)

thread.start()

threads.append(thread)

threadID += 1

# 填充队列

for word in range(1, 101): # 把100个任务放入队列中

workQueue.put(word)

# 等待队列清空

while not workQueue.empty():

pass

# 通知线程是时候退出

exitFlag = 1

# 等待所有线程完成

for t in threads:

t.join()

print("Exiting Main Thread")

实例2

import time

import threading

import queue

class Worker(threading.Thread):

def __init__(self, name, queue):

threading.Thread.__init__(self)

self.queue = queue

self.start() #执行run()

def run(self):

#循环,保证接着跑下一个任务

while True:

# 队列为空则退出线程

if self.queue.empty():

break

# 获取一个队列数据

foo = self.queue.get()

# 延时1S模拟你要做的事情

time.sleep(1)

# 打印

print(self.getName() + " process " + str(foo))

# 任务完成

self.queue.task_done()

# 队列

queue = queue.Queue()

# 加入100个任务队列

for i in range(100):

queue.put(i)

# 开10个线程

for i in range(10):

threadName = 'Thread' + str(i)

Worker(threadName, queue)

# 所有线程执行完毕后关闭

queue.join()



推荐阅读
  • 如何配置Unturned服务器及其消息设置
    本文详细介绍了Unturned服务器的配置方法和消息设置技巧,帮助用户了解并优化服务器管理。同时,提供了关于云服务资源操作记录、远程登录设置以及文件传输的相关补充信息。 ... [详细]
  • DNN Community 和 Professional 版本的主要差异
    本文详细解析了 DotNetNuke (DNN) 的两种主要版本:Community 和 Professional。通过对比两者的功能和附加组件,帮助用户选择最适合其需求的版本。 ... [详细]
  • 优化联通光猫DNS服务器设置
    本文详细介绍了如何为联通光猫配置DNS服务器地址,以提高网络解析效率和访问体验。通过智能线路解析功能,域名解析可以根据访问者的IP来源和类型进行差异化处理,从而实现更优的网络性能。 ... [详细]
  • 本文详细介绍如何使用Python进行配置文件的读写操作,涵盖常见的配置文件格式(如INI、JSON、TOML和YAML),并提供具体的代码示例。 ... [详细]
  • 本文详细介绍了如何在Linux系统上安装和配置Smokeping,以实现对网络链路质量的实时监控。通过详细的步骤和必要的依赖包安装,确保用户能够顺利完成部署并优化其网络性能监控。 ... [详细]
  • 本文详细介绍了 Dockerfile 的编写方法及其在网络配置中的应用,涵盖基础指令、镜像构建与发布流程,并深入探讨了 Docker 的默认网络、容器互联及自定义网络的实现。 ... [详细]
  • 在Linux系统中配置并启动ActiveMQ
    本文详细介绍了如何在Linux环境中安装和配置ActiveMQ,包括端口开放及防火墙设置。通过本文,您可以掌握完整的ActiveMQ部署流程,确保其在网络环境中正常运行。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 尽管某些细分市场如WAN优化表现不佳,但全球运营商路由器和交换机市场持续增长。根据最新研究,该市场预计在2023年达到202亿美元的规模。 ... [详细]
  • 网络攻防实战:从HTTP到HTTPS的演变
    本文通过一系列日记记录了从发现漏洞到逐步加强安全措施的过程,探讨了如何应对网络攻击并最终实现全面的安全防护。 ... [详细]
  • 360SRC安全应急响应:从漏洞提交到修复的全过程
    本文详细介绍了360SRC平台处理一起关键安全事件的过程,涵盖从漏洞提交、验证、排查到最终修复的各个环节。通过这一案例,展示了360在安全应急响应方面的专业能力和严谨态度。 ... [详细]
  • ImmutableX Poised to Pioneer Web3 Gaming Revolution
    ImmutableX is set to spearhead the evolution of Web3 gaming, with its innovative technologies and strategic partnerships driving significant advancements in the industry. ... [详细]
  • 本文详细分析了Hive在启动过程中遇到的权限拒绝错误,并提供了多种解决方案,包括调整文件权限、用户组设置以及环境变量配置等。 ... [详细]
  • PyCharm下载与安装指南
    本文详细介绍如何从官方渠道下载并安装PyCharm集成开发环境(IDE),涵盖Windows、macOS和Linux系统,同时提供详细的安装步骤及配置建议。 ... [详细]
  • 本文详细介绍了如何解决Uploadify插件在Internet Explorer(IE)9和10版本中遇到的点击失效及JQuery运行时错误问题。通过修改相关JavaScript代码,确保上传功能在不同浏览器环境中的一致性和稳定性。 ... [详细]
author-avatar
airchampion
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有