Mono准备好进入黄金时段了吗?

有人在大型或中型项目中使用过。net开源实现Mono吗?我想知道它是否已经为现实世界的生产环境做好了准备。它是否稳定、快速、兼容……足够用了吗?将项目移植到Mono运行时是否需要大量的努力,或者它是否真的足够兼容真的,只需要为微软的运行时编写代码就可以了?

24753 次浏览

在许多情况下,你可以获取现有的代码并在Mono上运行,特别是如果你正在移植一个ASP。网络应用程序。

在某些情况下,您可能需要全新的代码段才能使其工作。如果你使用System.Windows。例如,如果不修改表单,应用程序将无法工作。同样,如果您使用任何特定于windows的代码(例如,注册表访问代码)。但我认为最糟糕的是UI代码。这在麦金塔系统上尤其糟糕。

在桌面端,如果您承诺使用gtk#, Mono工作得很好。窗户。表单实现仍然有一些bug(例如,TrayIcon的不能工作),但它已经取得了很大的进步。此外,gtk#是一个比Windows窗体更好的工具包。

在web端,Mono已经实现了足够多的ASP。NET可以完美地运行大多数网站。这里的困难是找到一个在apache上安装了mod_mono的主机,或者如果你有shell访问你的主机,你自己做。

不管怎样,Mono都很棒,而且很稳定。

创建跨平台程序时需要记住的关键事项:

  • 使用gtk#而不是Windows。形式
  • 确保文件名的大小写正确
  • 使用Path.Separator而不是硬编码"\",也使用Environment.NewLine而不是"\n"
  • 不要使用任何P/Invoked调用Win32 API。
  • 不要使用Windows注册表。

它覆盖了。net 4.0,甚至包括了。net 4.5 api的一些特性,但是有一些领域我们选择不去实现,因为api被弃用了,新的替代品被创建了,或者范围太大了。以下api在Mono中不可用:

  • Windows Presentation Foundation
  • Windows Workflow Foundation(两个版本都不是)
  • 实体框架
  • 标准Web服务堆栈的WSE1/WSE2“附加组件”

此外,我们的WCF实现仅限于Silverlight支持的内容。

检查特定项目的最简单方法是运行单质迁移分析仪(MoMA)。好处是它会通知Mono团队那些会阻止你使用Mono的问题(如果有的话),这让他们优先考虑他们的工作。

我最近在SubSonic上运行了MoMA,只发现了一个问题——对Nullable类型的奇怪使用。这是一个很大的代码库,所以覆盖率非常高。

Mono在一些商业和开源产品中被积极使用。它在一些大型应用程序中使用,如维基百科和Mozilla开发者中心,并已在嵌入式应用程序中使用,如Sansa MP3播放器,并为数以千计的已发行游戏提供动力。

在语言层面,Mono编译器完全符合c# 5.0语言规范

这实际上取决于您从. net框架中使用的名称空间和类。我有兴趣将我的一个windows服务转换到我的电子邮件服务器(Suse)上运行,但我们遇到了几个尚未完全实现的api的硬障碍。Mono网站上的某个地方有一个图表,列出了所有的课程及其完成程度。如果你的申请被覆盖了,那就去申请吧。

当然,像任何其他应用程序一样,在做出全面承诺之前要进行原型设计和测试。

我们遇到的另一个问题是授权软件:如果您引用的是别人的DLL,那么您就无法通过编写代码来绕过隐藏在该程序集中的不兼容性。

你知道Mono 2.0预览版对Windows Forms 2.0的支持有多好吗?

从我使用它的一小部分来看,它似乎相对完整,几乎可用。它只是在一些地方看起来不太对,总的来说还是有点命中或错过。说实话,它和我们的一些表格一样好用,这让我很惊讶。

我可以想象,如果你有一个带有一些第三方组件的应用程序,你可能会被塞满。我怀疑很多供应商会在开发过程中考虑到Mono

例如:http://community.devexpress.com/forums/p/55085/185853.aspx

我们在一个需要在Linux上运行的项目中使用了它,但重用了我们在Managed c++中构建的一些。net库。我对它的成功感到非常惊讶。我们的主要可执行文件是用c#编写的,我们可以引用托管的c++二进制文件,没有问题。Windows和Linux之间c#代码的唯一区别是RS232串口代码。

我能想到的唯一大事件发生在大约一个月前。Linux版本有一个在Windows版本中看不到的内存泄漏。在做了一些手动调试之后(Linux上Mono的基本分析器没有多大帮助),我们能够将问题缩小到特定的代码块。我们最终修补了一个解决方案,但我仍然需要找一些时间回去,找出泄漏的根本原因是什么。

MoMA是一个很好的工具,就像其他人建议的那样。目前最大的不兼容性来源是将DllImport(或P/Invoke)导入Win32库的应用程序。有些程序集没有实现,但大多数程序集仅适用于windows,在Linux上确实没有意义。我认为可以肯定地说,大多数ASP。NET应用程序可以在Mono上运行,只需进行有限的修改。

(披露:我为Mono本身做出了贡献,也编写了在Mono上运行的应用程序。)

对于公认答案的建议现在有点过时了。

  • windows窗体的实现现在已经很不错了。(请参阅Paint-Mono获得Paint.net的一个端口,这是一个相当复杂的Windows表单应用程序。所需要的只是一些P-Invoke和不支持的系统调用的模拟层)。
  • 路径。结合以及路径。分隔符连接路径和文件名。
  • windows注册表是可以的,只要你只是用它来存储和检索你的应用程序的数据(也就是说,你不能从它得到任何关于windows的信息,因为它基本上是一个Mono应用程序的注册表)。

这里有几个场景需要考虑:(a)如果你正在移植一个现有的应用程序,并且想知道Mono是否足够好来完成这个任务;(b)你开始写一些新代码,你想知道Mono是否足够成熟。

对于第一种情况,你可以使用Mono迁移分析工具 (Moma)来评估你的应用程序在Mono上运行的距离。如果评估结果令人满意,你就应该开始测试和QA工作,并准备发布。

如果你的评估返回的报告强调了在Mono中缺失的功能或语义上的显著差异,你将不得不评估代码是否可以改编,重写,或者在最坏的情况下,你的应用程序是否可以在功能减少的情况下工作。

根据我们基于用户提交的Moma统计(这是记忆),大约50%的应用程序可以开箱运行,大约25%的应用程序需要一周的工作(重构,调整),另外15%的应用程序需要认真地重做代码块,其余的应用程序不值得费力移植,因为它们与Win32绑定得太紧密了。在这一点上,您要么从零开始,要么业务决策将推动使您的代码可移植的努力,但我们谈论的是几个月的工作(至少从我们拥有的报告来看)。

如果您从头开始,情况就简单多了,因为您将只使用Mono中存在的api。只要你继续使用支持的堆栈(基本上是。net 2.0,加上3.5的所有核心升级,包括LINQ和System)。核心,加上任何Mono跨平台api)你会很好。

每隔一段时间,你可能会在Mono中遇到bug或限制,你可能不得不解决它们,但这与其他系统没有什么不同。

至于可移植性:ASP。NET应用程序更容易移植,因为它们对Win32几乎没有依赖,你甚至可以使用SQL server或其他流行的数据库(Mono中有很多捆绑的数据库提供商)。

窗户表单移植有时比较棘手,因为开发人员喜欢逃离。net沙盒,用P/Invoke来配置一些有用的东西,比如用wParam中BCD格式编码的两个bezier点来表示改变光标闪烁速率。或者类似的垃圾。

如果你想使用WPF,那你就不走运了,Mono目前还没有计划实现它。

http://www.mono-project.com/WPF

是的,它确实是(如果你小心的话) 我们在Ra-Ajax中支持Mono (Ajax库在http://ra-ajax.org中找到),我们基本上没有任何问题。你需要小心一些来自。net的“最疯狂的东西”,比如WSE等,也可能有相当多的现有项目不会100%与Mono兼容,但如果你在开发过程中测试它们,新项目大部分会与Mono兼容而没有问题。通过使用Mono来支持Linux等的好处真的很酷;)

我认为支持Mono的很大一部分秘密是从一开始就使用正确的工具,例如ActiveRecord, log4net, ra-ajax等。

不幸的是,对于我们正在构建的应用程序类型来说,Mono似乎还没有准备好投入生产。总的来说,我们对它印象深刻,对它在Windows和EC2机器上的性能印象深刻,然而,我们的程序在Windows和linux上都因垃圾收集错误而崩溃。

错误信息是:“GC中的致命错误:太多堆节”,这里是一个链接,其他人以略微不同的方式遇到这个问题:

http://bugzilla.novell.com/show_bug.cgi?id=435906

我们在Mono中运行的第一段代码是我们开发的一个简单的编程挑战……代码将大约10mb的数据加载到一些数据结构(例如HashSets)中,然后对数据运行10个查询。我们将这些查询运行100次以计算它们的时间并获得平均值。

在Windows上,代码在第55个查询时崩溃。在linux上它可以工作,但一旦我们转移到更大的数据集,它也会崩溃。

这段代码非常简单,例如,把一些数据放入哈希集,然后查询这些哈希集等,所有本机c#,没有不安全的,没有API调用。在微软CLR上,它从来不会崩溃,在巨大的数据集上运行1000次也很好。

我们的一个人给米格尔发了邮件,附上了导致问题的代码,还没有回复。:(

似乎很多人也遇到过这个问题,但没有解决方案——有人建议用不同的GC设置重新编译Mono,但这似乎增加了崩溃的阈值。

请查看www.plasticscm.com。所有东西(客户端、服务器、GUI、合并工具)都是在mono上编写的。

我个人在黄金时段使用Mono。 我运行单服务器处理千兆字节的udp/tcp数据处理相关任务,不能再高兴了

有一些特殊之处,其中最烦人的事情之一是,由于Mono的当前状态,你不能“构建”你的msbuild文件:

  • MonoDevelop (IDE)有一些部分msbuild支持,但基本上会对任何“真实”构建conf超出一个简单的hello-world(自定义构建任务,动态“属性”,如$(SolutionDir),真实配置,以命名几个死角)
  • xbuild 本应如此这个单供应的msbuild-完全兼容的build-系统甚至更可怕,所以从命令行构建实际上是一个比使用GUI更糟糕的体验,这对于Linux环境来说是一个非常“非正统”的状态…

一旦/在你的东西真正构建的过程中,你可能会看到一些应该支持的代码的荒野:

  • 编译器在某些构造上出错
  • 一些更高级的/新的。net类向你抛出意想不到的垃圾(XLinq的人吗?)
  • 一些不成熟的运行时“特性”(3GB堆限制在x64…WTF !)

但heaving表示,一般来说,事情很快就会开始起作用,而且解决方案/变通方法很多

一旦你克服了这些最初的障碍,我的经验是,mono很摇滚,并在每次迭代中变得越来越好

我有使用mono运行的服务器,每天处理300GB的数据,有大量的p/调用,通常来说要做大量的工作,并且保持5-6个月,即使使用“最先进的”mono。

希望这能有所帮助。

不,单核细胞增多症还不能胜任严肃的工作。我用f#在Windows上写了几个程序,然后在Mono上运行。这些程序相当密集地使用磁盘、内存和cpu。我看到在单库(托管代码)中崩溃,在本地代码中崩溃,在虚拟机中崩溃。当mono运行时,程序比Windows中的。net慢了至少两倍,并且占用了更多的内存。在严肃的工作中远离单核细胞增多症。

单核细胞增多症很好,但据我所知,它不稳定。它可以工作,但是当你给单一进程一个严肃的工作去做时,它就会出错。

TL;DR -如果你:

  • 在多线程环境中使用AppDomains(程序集加载/卸载)
  • 无法维持“听之任之”的模式
  • 在流程运行期间偶尔经历重载事件

下面是事实。

我们在RHEL5和Ubuntu上使用mono-2.6.7 (.net v 3.5),在我看来,它是由Novell构建的最稳定的版本。它有一个问题卸载AppDomains (segfaults),然而,它失败非常罕见,到目前为止,这是可以接受的(由我们)。

好的。但是如果你想使用。net 4.0的特性,你必须切换到2.10版本。X,或者3。X,这就是问题开始的地方。

与2.6.7相比,新版本简直无法接受。我编写了一个简单的压力测试应用程序来测试mono安装。

它在这里,并附有使用指令:https://github.com/head-thrash/stress_test_mono

它使用线程池工作线程。Worker将dll加载到AppDomain并尝试做一些数学工作。有些工作是多线程的,有些是单线程的。几乎所有的工作都是cpu限制的,尽管有一些从磁盘读取文件。

结果不是很好。事实上,对于3.0.12版本:

  • sgen GC段错误几乎立即处理
  • 带有boehm的Mono存活时间更长(从2到5小时),但最终会出现段故障

如上所述,sgen gc不能工作(mono从源代码构建):

* Assertion: should not be reached at sgen-scan-object.h:111


Stacktrace:




Native stacktrace:


mono() [0x4ab0ad]
/lib/x86_64-linux-gnu/libpthread.so.0(+0xfcb0) [0x2b61ea830cb0]
/lib/x86_64-linux-gnu/libc.so.6(gsignal+0x35) [0x2b61eaa74425]
/lib/x86_64-linux-gnu/libc.so.6(abort+0x17b) [0x2b61eaa77b8b]
mono() [0x62b49d]
mono() [0x62b5d6]
mono() [0x5d4f84]
mono() [0x5cb0af]
mono() [0x5cb2cc]
mono() [0x5cccfd]
mono() [0x5cd944]
mono() [0x5d12b6]
mono(mono_gc_collect+0x28) [0x5d16f8]
mono(mono_domain_finalize+0x7c) [0x59fb1c]
mono() [0x596ef0]
mono() [0x616f13]
mono() [0x626ee0]
/lib/x86_64-linux-gnu/libpthread.so.0(+0x7e9a) [0x2b61ea828e9a]
/lib/x86_64-linux-gnu/libc.so.6(clone+0x6d) [0x2b61eab31ccd]

对于boehm segfaults -例如(Ubuntu 13.04, mono从源代码构建):

mono: mini-amd64.c:492: amd64_patch: Assertion `0' failed.
Stacktrace:
at <unknown> <0xffffffff>
at System.Collections.Generic.Dictionary`2.Init (int,System.Collections.Generic.IEqualityComparer`1<TKey>) [0x00012] in /home/bkmz/my/mono/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:264
at System.Collections.Generic.Dictionary`2..ctor () [0x00006] in /home/bkmz/my/mono/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:222
at System.Security.Cryptography.CryptoConfig/CryptoHandler..ctor (System.Collections.Generic.IDictionary`2<string, System.Type>,System.Collections.Generic.IDictionary`2<string, string>) [0x00014] in /home/bkmz/my/mono/mcs/class/corlib/System.Security.Cryptography/Crypto
Config.cs:582
at System.Security.Cryptography.CryptoConfig.LoadConfig (string,System.Collections.Generic.IDictionary`2<string, System.Type>,System.Collections.Generic.IDictionary`2<string, string>) [0x00013] in /home/bkmz/my/mono/mcs/class/corlib/System.Security.Cryptography/CryptoCo
nfig.cs:473
at System.Security.Cryptography.CryptoConfig.Initialize () [0x00697] in /home/bkmz/my/mono/mcs/class/corlib/System.Security.Cryptography/CryptoConfig.cs:457
at System.Security.Cryptography.CryptoConfig.CreateFromName (string,object[]) [0x00027] in /home/bkmz/my/mono/mcs/class/corlib/System.Security.Cryptography/CryptoConfig.cs:495
at System.Security.Cryptography.CryptoConfig.CreateFromName (string) [0x00000] in /home/bkmz/my/mono/mcs/class/corlib/System.Security.Cryptography/CryptoConfig.cs:484
at System.Security.Cryptography.RandomNumberGenerator.Create (string) [0x00000] in /home/bkmz/my/mono/mcs/class/corlib/System.Security.Cryptography/RandomNumberGenerator.cs:59
at System.Security.Cryptography.RandomNumberGenerator.Create () [0x00000] in /home/bkmz/my/mono/mcs/class/corlib/System.Security.Cryptography/RandomNumberGenerator.cs:53
at System.Guid.NewGuid () [0x0001e] in /home/bkmz/my/mono/mcs/class/corlib/System/Guid.cs:492

或(RHEL5, mono是从rpm这里ftp://ftp.pbone.net/mirror/ftp5.gwdg.de/pub/opensuse/repositories/home%3A/vmas%3A/mono-centos5取的)

Assertion at mini.c:3783, condition `code' not met
Stacktrace:
at <unknown> <0xffffffff>
at System.IO.StreamReader.ReadBuffer () [0x00012] in /usr/src/redhat/BUILD/mono-3.0.3/mcs/class/corlib/System.IO/StreamReader.cs:394
at System.IO.StreamReader.Peek () [0x00006] in /usr/src/redhat/BUILD/mono-3.0.3/mcs/class/corlib/System.IO/StreamReader.cs:429
at Mono.Xml.SmallXmlParser.Peek () [0x00000] in /usr/src/redhat/BUILD/mono-3.0.3/mcs/class/corlib/Mono.Xml/SmallXmlParser.cs:271
at Mono.Xml.SmallXmlParser.Parse (System.IO.TextReader,Mono.Xml.SmallXmlParser/IContentHandler) [0x00020] in /usr/src/redhat/BUILD/mono-3.0.3/mcs/class/corlib/Mono.Xml/SmallXmlParser.cs:346
at System.Security.Cryptography.CryptoConfig.LoadConfig (string,System.Collections.Generic.IDictionary`2<string, System.Type>,System.Collections.Generic.IDictionary`2<string, string>) [0x00021] in /usr/src/redhat/BUILD/mono-3.0.3/mcs/class/corlib/System.Security.Cryptog
raphy/CryptoConfig.cs:475
at System.Security.Cryptography.CryptoConfig.Initialize () [0x00697] in /usr/src/redhat/BUILD/mono-3.0.3/mcs/class/corlib/System.Security.Cryptography/CryptoConfig.cs:457
at System.Security.Cryptography.CryptoConfig.CreateFromName (string,object[]) [0x00027] in /usr/src/redhat/BUILD/mono-3.0.3/mcs/class/corlib/System.Security.Cryptography/CryptoConfig.cs:495
at System.Security.Cryptography.CryptoConfig.CreateFromName (string) [0x00000] in /usr/src/redhat/BUILD/mono-3.0.3/mcs/class/corlib/System.Security.Cryptography/CryptoConfig.cs:484
at System.Security.Cryptography.RandomNumberGenerator.Create (string) [0x00000] in /usr/src/redhat/BUILD/mono-3.0.3/mcs/class/corlib/System.Security.Cryptography/RandomNumberGenerator.cs:59
at System.Security.Cryptography.RandomNumberGenerator.Create () [0x00000] in /usr/src/redhat/BUILD/mono-3.0.3/mcs/class/corlib/System.Security.Cryptography/RandomNumberGenerator.cs:53
at System.Guid.NewGuid () [0x0001e] in /usr/src/redhat/BUILD/mono-3.0.3/mcs/class/corlib/System/Guid.cs:483
at System.Runtime.Remoting.RemotingServices.NewUri () [0x00020] in /usr/src/redhat/BUILD/mono-3.0.3/mcs/class/corlib/System.Runtime.Remoting/RemotingServices.cs:356
at System.Runtime.Remoting.RemotingServices.Marshal (System.MarshalByRefObject,string,System.Type) [0x000ba] in /usr/src/redhat/BUILD/mono-3.0.3/mcs/class/corlib/System.Runtime.Remoting/RemotingServices.cs:329
at System.AppDomain.GetMarshalledDomainObjRef () [0x00000] in /usr/src/redhat/BUILD/mono-3.0.3/mcs/class/corlib/System/AppDomain.cs:1363

这两种失败在某种程度上都与AppDomains逻辑有关,所以,你应该在mono中远离它们。

顺便说一句,测试的程序在Windows机器上运行24小时,在MS . net 4.5环境下没有任何故障。

总之,我想说的是,谨慎使用单倍体。它从第一眼就能起作用,但无论何时都很容易失败。你会留下一堆核心转储和对开源项目的重大信心损失。