NIO - 使用选择器

http://zachary-guo.iteye.com/blog/1565856

在详细了解 Selector API 之前,你需要知道一点和 Selector 内部工作原理相关的知识。就像在 NIO - 使用选择键 中]探讨的那样,选择器维护着注册过的通道的集合,并且这些注册关系中的任意一个都是封装在 SelectionKey 对象中的。每一个 Selector 对象维护三个键的集合: 

Java代码   收藏代码
  1. public abstract class Selectory {  
  2.     // This is a partial API listing  
  3.     public abstract Set keys();  
  4.     public abstract Set selectedKeys();  
  5.     public abstract int select() throws IOException;  
  6.     public abstract int select(long timeout) throws IOException;  
  7.     public abstract int selectNow() throws IOException;  
  8.     public abstract void wakeup();  
  9. }  

  • 已注册的键的集合(Registered key set)        与选择器关联的已经注册的键的集合。并不是所有注册过的键都仍然有效。这个集合通过 keys() 方法返回,并且可能是空的。这个已注册的键的集合不是可以直接修改的;试图这么做的话将引 java.lang.UnsupportedOperationException。 
           
  • 已选择的键的集合(Selected key set)        已注册的键的集合的子集。这个集合的每个成员都是相关的通道被选择器(在前一个选择操作中)判断为已经准备好的,并且包含于键的 interest 集合中的操作。这个集合通过 selectedKeys() 方法返回(并有可能是空的)。 
            
            不要将已选择的键的集合与 ready 集合弄混了。这是一个键的集合,每个键都关联一个已经准备好至少一种操作的通道。每个键都有一个内嵌的 ready 集合,指示了所关联的通道已经准备好的操作。 
            
            键可以直接从这个集合(已选择的键的集合)中移除,但不能添加。试图向已选择的键的集合中添加元素将抛出 java.lang.UnsupportedOperationException。 
           
  • 已取消的键的集合(Cancelled key set)        已注册的键的集合的子集,这个集合包含了 cancel() 方法(SelectionKey 类的方法)被调用过的键(这个键已经被无效化),但它们还没有被注销 。这个集合是选择器对象的私有成员,因而无法直接访问。 
        在一个刚初始化的 Selector 对象中,以上提到的三个集合都是空的。  

    ◇ 选择过程  
        Selector 类的核心是选择过程。基本上来说,选择器是对 select()、poll() 等本地调用(native call)或者类似的操作系统特定的系统调用的一个包装。但是 Selector 所作的不仅仅是简单地向本地代码传送参数。它对每个选择操作应用了特定的过程。对这个过程的理解是合理地管理键和它们所表示的状态信息的基础。  

        Selector 类的 select() 方法有以下三种不同的形式:  
  • int select()        这种调用在没有通道就绪时将无限阻塞。一旦至少有一个已注册的通道就绪,选择器的选择键就会被更新,并且每个就绪的通道的 ready 集合也将被更新。返回值将会是已经确定就绪的通道的数目。正常情况下,这些方法将返回一个非零的值,因为直到一个通道就绪前它都会阻塞。 
           
  • int select(long timeout)        这种调用与之前的例子完全相同,除了如果在你提供的超时时间(以毫秒计算)内没有通道就绪时,它将返回 0。如果一个或者多个通道在时间限制终止前就绪,键的状态将会被更新,并且方法会在那时立即返回。将超时参数指定为 0 表示将无限期等待,那么它就在各个方面都等同于使用无参数版本的 select() 了。 
           
  • int selectNow()        这种种形式是完全非阻塞的。selectNow() 方法执行就绪检查过程,但不阻塞。如果当前没有通道就绪,它将立即返回 0。 
        选择操作是当三种形式的 select() 中的任意一种被调用时,由选择器执行的。不管是哪一种形式的调用,下面步骤将被执行:  
        1. 已取消的键的集合将会被检查。如果它是非空的,每个已取消的键的集合中的键将从另外两个集合中移除,并且相关的通道将被注销。这个步骤结束后,已取消的键的集合将是空的。  

        2. 已注册的键的集合中的键的 interest 集合将被检查。在这个步骤中的检查执行过后,对 interest 集合的改动不会影响剩余的检查过程。  
        一旦就绪条件被定下来,底层操作系统将会进行查询,以确定每个通道所关心的操作的真实就绪状态。依赖于特定的 select() 方法调用,如果没有通道已经准备好,线程可能会在这时阻塞,通常会有一个超时值。对于那些还没准备好的通道将不会执行任何的操作。对于那些操作系统指示至少已经准备好 interest 集合中的一种操作的通道,将执行以下两种操作中的一种:  

  • 如果通道的键还没有处于已选择的键的集合中,那么键的 ready 集合将被清空,然后表示操作系统发现的当前通道已经准备好的操作的比特掩码将被设置。
  • 否则,也就是键在已选择的键的集合中。键的 ready 集合将被表示操作系统发现的当前已经准备好的操作的比特掩码更新。所有之前的已经不再是就绪状态的操作不会被清除。事实上,所有的比特位都不会被清理。由操作系统决定的 ready 集合是与之前的 ready 集合按位分离的,一旦键被放置于选择器的已选择的键的集合中,它的 ready 集合将是累积的。比特位只会被设置,不会被清理。假设之前的 ready 集合为 100,此次 010 的操作已就绪,此时的 ready 集合为 110,而不是 010。这就是累积,不会被清理。
        3. 步骤 2 可能会花费很长时间,特别是所激发的线程处于休眠状态时。与该选择器相关的键可能会同时被取消。当步骤 2 结束时,步骤 1 将重新执行,以完成任意一个在选择进行的过程中,键已经被取消的通道的注销。  

        4. select 操作返回的值是 ready 集合在步骤 2 中被修改的键的数量,而不是已选择的键的集合中的通道的总数。返回值不是已准备好的通道的总数,而是从上一个 select() 调用之后进入就绪状态的通道的数量。之前的调用中就绪的,并且在本次调用中仍然就绪的通道不会被计入,而那些在前一次调用中已经就绪但已经不再处于就绪状态的通道也不会被计入。这些通道可能仍然在已选择的键的集合中,但不会被计入返回值中。返回值可能是 0。  

    ◇ 停止选择过程  
        Selector 的 API 中的最后一个方法, wakeup(),提供了使线程从被阻塞的 select() 方法中优雅地退出的能力:  
Java代码   收藏代码
  1. public abstract class Selector {  
  2.     // This is a partial API listing  
  3.     public abstract void wakeup();  
  4. }  

        有三种方式可以唤醒在 select() 方法中睡眠的线程:  
  • 调用 wakeup()        调用 Selector 对象的 wakeup() 方法将使得选择器上的第一个还没有返回的选择操作立即返回。 
           
  • 调用 close()        如果选择器的 close() 方法被调用,那么任何一个在选择操作中阻塞的线程都将被唤醒,就像 wakeup() 方法被调用了一样。与选择器相关的通道将被注销,而键将被取消。 
           
  • 调用 interrupt()        如果睡眠中的线程的 interrupt() 方法被调用,它的返回状态将被设置。如果被唤醒的线程之后将试图在通道上执行 I/O 操作,通道将立即关闭,然后线程将捕捉到一个异常。使用 wakeup() 方法将会优雅地将一个在 select() 方法中睡眠的线程唤醒。如果你想让一个睡眠的线程在直接中断之后继续执行,需要执行一些步骤来清理中断状态(参见 Thread.interrupted() 的相关文档)。 

        Selector 对象将捕捉 InterruptedException 异常并调用 wakeup() 方法。请注意这些方法中的任意一个都不会关闭任何一个相关的通道。中断一个选择器与中断一个通道是不一样的。选择器不会改变任意一个相关的通道,它只会检查它们的状态。当一个在 select() 方法中睡眠的线程中断时,对于通道的状态而言,是不会产生歧义的。

    ◇ 管理选择键  
        选择是累积的。一旦一个选择器将一个键添加到它的已选择的键的集合中,它就不会移除这个键。并且,一旦一个键处于已选择的键的集合中,这个键的 ready 集合将只会被设置,而不会被清理。乍一看,这好像会引起麻烦,因为选择操作可能无法表现出已注册的通道的正确状态。它提供了极大的灵活性,但把合理地管理键以确保它们表示的状态信息不会变得陈旧的任务交给了程序员。  

        合理地使用选择器的秘诀是理解选择器维护的选择键集合所扮演的角色(参见 选择过程 小节,特别是选择过程的第二步)。最重要的部分是当键已经不再在已选择的键的集合中时将会发生什么。当通道上的至少一个感兴趣的操作就绪时,键的 ready 集合就会被清空,并且当前已经就绪的操作将会被添加到 ready 集合中。该键之后将被添加到已选择的键的集合中。  

        清理一个 SelectKey 的 ready 集合的方式是将这个键从已选择的键的集合中移除。选择键的就绪状态只有在选择器对象在选择操作过程中才会修改。处理思想是只有在已选择的键的集合中的键才被认为是包含了合法的就绪信息的。这些信息将在键中长久地存在,直到键从已选择的键的集合中移除,以通知选择器你已经看到并对它进行了处理。如果下一次通道的一些感兴趣的操作发生时,键将被重新设置以反映当时通道的状态并再次被添加到已选择的键的集合中。  

        这种框架提供了很多灵活性。通常的做法是在选择器上调用一次 select 操作(这将更新已选择的键的集合),然后遍历 selectKeys() 方法返回的键的集合。在按顺序进行检查每个键的过程中,相关的通道也根据键的就绪集合进行处理。然后键将从已选择的键的集合中被移除(通过在 Iterator 对象上调用 remove() 方法),然后检查下一个键。完成后,通过再次调用 select() 方法重复这个循环。

使用Python来安装geopandas包时,由于geopandas依赖于几个其他的Python库(如GDAL, Fiona, Pyproj, Shapely等),因此安装过程可能需要一些额外的步骤。以下是一个基本的安装指南,适用于大多数用户: 使用pip安装 确保Python和pip已安装: 首先,确保你的计算机上已安装了Python和pip。pip是Python的包管理工具,用于安装和管理Python包。 安装依赖库: 由于geopandas依赖于GDAL, Fiona, Pyproj, Shapely等库,你可能需要先安装这些库。通常,你可以通过pip直接安装这些库,但有时候可能需要从其他源下载预编译的二进制包(wheel文件),特别是GDAL和Fiona,因为它们可能包含一些系统级的依赖。 bash pip install GDAL Fiona Pyproj Shapely 注意:在某些系统上,直接使用pip安装GDAL和Fiona可能遇到问题,因为它们需要编译一些C/C++代码。如果遇到问题,你可以考虑使用conda(一个Python包、依赖和环境管理器)来安装这些库,或者从Unofficial Windows Binaries for Python Extension Packages这样的网站下载预编译的wheel文件。 安装geopandas: 在安装了所有依赖库之后,你可以使用pip来安装geopandas。 bash pip install geopandas 使用conda安装 如果你正在使用conda作为你的Python包管理器,那么安装geopandas和它的依赖可能更简单一些。 创建一个新的conda环境(可选,但推荐): bash conda create -n geoenv python=3.x anaconda conda activate geoenv 其中3.x是你希望使用的Python版本。 安装geopandas: 使用conda-forge频道来安装geopandas,因为它提供了许多地理空间相关的包。 bash conda install -c conda-forge geopandas 这条命令自动安装geopandas及其所有依赖。 注意事项 如果你在安装过程中遇到任何问题,比如编译错误或依赖问题,请检查你的Python版本和pip/conda的版本是否是最新的,或者尝试在不同的环境中安装。 某些库(如GDAL)可能需要额外的系统级依赖,如地理空间库(如PROJ和GEOS)。这些依赖可能需要单独安装,具体取决于你的操作系统。 如果你在Windows上遇到问题,并且pip安装失败,尝试从Unofficial Windows Binaries for Python Extension Packages网站下载相应的wheel文件,并使用pip进行安装。 脚本示例 虽然你的问题主要是关于如何安装geopandas,但如果你想要一个Python脚本来重命名文件夹下的文件,在原始名字前面加上字符串"geopandas",以下是一个简单的示例: python import os # 指定文件夹路径 folder_path = 'path/to/your/folder' # 遍历文件夹中的文件 for filename in os.listdir(folder_path): # 构造原始文件路径 old_file_path = os.path.join(folder_path, filename) # 构造新文件名 new_filename = 'geopandas_' + filename # 构造新文件路径 new_file_path = os.path.join(folder_path, new_filename) # 重命名文件 os.rename(old_file_path, new_file_path) print(f'Renamed "{filename}" to "{new_filename}"') 请确保将'path/to/your/folder'替换为你想要重命名文件的实际文件夹路径。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值