我已经遇到过几次这个问题,除了微不足道的解决方案外,我无法找到任何解决方案(见下文)。
假设一台计算机正在运行2个以上的R实例,因为2个以上用户或1个用户正在运行多个进程,并且一个实例执行update.packages()
。我已经有好几次了,其他的例子可能会被严重犯规。正在更新的包不会以任何影响计算的方式更改功能,但不知何故,出现了一个大问题。
琐碎的解决方案(解决方案0)是在执行update.packages()
时终止R的所有实例。这有2个以上的问题。首先,必须终止R个实例。其次,人们甚至可能无法识别这些实例在哪里运行(请参阅更新1)。
假设正在执行的代码的行为不会改变(例如,包更新都是有益的-它们只会修复错误、提高速度、减少RAM和授予独角兽),是否有办法热交换对其他进程影响较小的新版本包?
除了R之外,我还有两个候选解决方案:
解决方案1是使用一个临时库路径,然后删除旧的旧库并将新库移到其位置。这样做的缺点是删除+移动可能会导致一段时间内什么都不可用。
解决方案2是使用符号链接指向库(或库层次结构),然后用指向更新包所在的新库的指针覆盖符号链接。这似乎会导致更少的软件包停机时间,即操作系统覆盖符号链接所需的时间。这样做的缺点是,它需要在管理符号链接时更加小心,而且是特定于平台的。
我怀疑,通过巧妙地使用.libPaths()
,可以将解决方案#1修改为#2,但这似乎需要而不是调用update.packages()
,而是编写一个新的更新程序,查找过时的包,将它们安装到临时库中,然后更新库路径。这样做的好处是,可以将现有进程约束为其启动时的.libPaths()
(即,如果没有该实例中的一些明确干预,更改R所知道的库路径可能不会传播到那些已经在运行的实例)。
更新1。在示例场景中,两个竞争的R实例位于同一台机器上。这不是一个要求:就我对更新的理解而言,如果两者共享相同的库,即共享驱动器上的相同目录,那么即使R的另一个实例在另一台机器上,更新仍然可能导致问题。因此,人们可能会意外地杀死一个R进程,甚至看不到它。
在生产环境中,您可能需要保留至少两个版本,即当前版本和前一个版本,以便在出现问题时能够快速切换回旧版本。不会覆盖或删除任何内容。对于整个R生态系统来说,这样做更容易:你会有几个目录,比如"R-2.14.1-2011-12-22"、"R-2.14.1/2012-01-27"等,每个目录都包含所有内容(R可执行文件和所有包)。这些目录永远不会更新:如果需要更新,就会创建一个新目录。(有些文件系统提供"快照",可以让您拥有许多非常相似的目录,而不会过度占用磁盘空间。)
当用户启动R时,可以在用户端从一个版本切换到另一个版本,方法是用使用正确版本的脚本替换R可执行文件,或者将PATH环境变量设置为指向所需版本。这样可以确保给定的会话始终看到相同版本的所有内容。
我强烈猜测这是没有办法的。
尤其是当一个包包含已编译的代码时,你不能在DLL使用时删除和替换它,并期望它仍然可以工作。对这些函数的R调用所使用的DLL中的所有指针都会要求特定的内存位置,却发现它莫名其妙地消失了。(注意——虽然我在这里使用术语"DLL",但我指的是非Windows特定意义上的DLL,例如在?getLoadedDLLs
的帮助文件中使用的DLL。"共享库"可能是更好的通用术语。)
(我的怀疑得到了一些来自R for Windows常见问题解答的证实,该问题解答报告称"Windows在加载程序包时锁定了它的DLL",这可能导致update.packages()
失败。)
我不确定R的惰性加载机制是如何实现的,但想象一下,它也可能会因为删除它希望在机器中的特定地址找到的对象而被破坏。
其他对计算机内部更了解的人肯定会给出比这更好的答案,但这些都是我的想法。
这是我昨天在Windows7上遇到的一个场景。
- 我正在进行R会话
- 打开软件包手册的PDF
- 关闭所有R会话。忘记关闭软件包手册PDF
- 打开R的新实例,运行update.packages()
安装当然失败了,因为Windows仍然打开pdf并且无法覆盖它…