有人在大型或中型项目中使用过 Mono(开源 .NET 实现)吗?我想知道它是否适合现实世界的生产环境。稳定、快速、兼容、...够用吗?将项目移植到 Mono 运行时是否需要花费很多精力,或者确实如此, 真的 是否足够兼容以获取并运行已为 Microsoft 运行时编写的代码?

有帮助吗?

解决方案

有几种情况需要考虑:(a) 如果您正在移植现有应用程序并想知道 Mono 是否足以胜任此任务;(b) 你开始编写一些新代码,并且你想知道 Mono 是否足够成熟。

对于第一种情况,您可以使用 Mono 迁移分析器工具 (Moma) 评估您的应用程序距离在 Mono 上运行还有多远。如果评估结果非常出色,您应该开始测试和质量检查并准备发货。

如果您的评估返回的报告突出显示了 Mono 中缺失或语义显着不同的功能,您将必须评估代码是否可以调整、重写,或者在最坏的情况下您的应用程序是否可以在功能减少的情况下工作。

根据我们基于用户提交的 Moma 统计数据(这是根据记忆),大约 50% 的应用程序开箱即用,大约 25% 需要大约一周的工作量(重构、调整),另外 15% 需要认真致力于重做代码块,其余部分不值得费心移植,因为它们与 Win32 的联系如此紧密。那时,要么你从零开始,要么业务决策将推动你的代码可移植,但我们正在谈论值得几个月的工作(至少从我们拥有的报告来看)。

如果您从头开始,情况会简单得多,因为您将只使用 Mono 中提供的 API。只要您继续使用受支持的堆栈(几乎是 .NET 2.0,加上 3.5 中的所有核心升级,包括 LINQ 和 System.Core,以及任何 Mono 跨平台 API),您就会没事。

每隔一段时间,您可能会遇到 Mono 中的错误或限制,并且您可能必须解决它们,但这与任何其他系统没有什么不同。

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

Windows.Forms 移植有时会比较棘手,因为开发人员喜欢逃离 .NET 沙箱,并绞尽脑汁进行 P/Invoke 来配置一些有用的内容,例如更改光标闪烁率(表示为 wParam 中以 BCD 形式编码的两个贝塞尔点)。或者类似的垃圾。

其他提示

它的覆盖范围相当广泛,直至 .NET 4.0,甚至包括 .NET 4.5 API 的一些功能,但由于 API 已弃用、正在创建新的替代方案或范围太大,我们选择不实现一些领域。大的。以下 API 在 Mono 中不可用:

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

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

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

我最近在 SubSonic 上运行 MoMA,只发现一个问题 - Nullable 类型的奇怪使用。这是一个很大的代码库,所以那里的覆盖范围非常令人印象深刻。

Mono 正在积极使用 一些商业和开源产品. 。它在一些大型应用程序中使用,例如 维基百科和 Mozilla 开发者中心, ,并已用于嵌入式应用程序,例如 Sansa MP3 播放器,并为数千种已发布的游戏提供支持。

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

在桌面端,如果您致力于使用 GTK#,Mono 效果会很好。Windows.Forms 实现仍然存在一些问题(例如,TrayIcon 不起作用),但它已经取得了长足的进步。此外,GTK# 是一个比 Windows Forms 更好的工具包。

在 Web 方面,Mono 已经实现了足够的 ASP.NET 来完美运行大多数网站。这里的困难是找到一个在 apache 上安装了 mod_mono 的主机,或者如果你有主机的 shell 访问权限,你就自己做。

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

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

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

我个人在黄金时段环境中使用 Mono。我运行单声道服务器来处理千兆字节的 udp/tcp 数据处理相关任务,并且非常高兴。

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

  • MonoDevelop(IDE)有一些部分的msbuild支持,但基本上会在任何“真正的”构建conf上超出简单的hello-world(自定义构建任务,像$(SolutionDir)这样的动态“属性”,真实配置等等) -结束)
  • xbuild 其中 本来应该 mono-supplied-msbuild-完全兼容的构建系统甚至更可怕,因此从命令行构建实际上比使用 GUI 更糟糕,对于 Linux 环境来说,这是一种非常“非正统”的联盟状态。 ..

一旦/在实际构建你的东西​​期间,你可能会看到一些荒野,即使是应该支持的代码,例如:

  • 编译器对某些结构感到厌烦
  • 某些更高级/新的 .NET 类会给你带来意想不到的垃圾(XLinq 有人吗?)
  • 一些不成熟的运行时“功能”(x64 上的 3GB 堆限制...卧槽!)

但希辛说,一般来说,事情开始运作得很快,并且解决方案/变通方法很丰富.

一旦你克服了最初的障碍,我的经验是单声道摇滚,并且每次迭代都会变得更好.

我的服务器运行着单声道,每天处理 300GB 的数据,有大量的 p/调用,一般来说,即使使用“前沿”单声道,也需要做大量的工作并保持正常运行 5-6 个月。

希望这可以帮助。

已接受答案的建议现在有点过时了。

  • Windows 窗体的实现现在已经相当不错了。(看 单色绘画 Paint.net 的一个端口,这是一个非常复杂的 Windows 窗体应用程序。所需要的只是一些 P-Invoke 和不受支持的系统调用的模拟层。
  • Path.Combine 以及 Path.Seperator 连接路径和文件名。
  • Windows 注册表是可以的,只要您仅使用它来存储和检索应用程序中的数据(即您无法从中获取有关 Windows 的任何信息,因为它基本上是 Mono 应用程序的注册表)。

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

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

嗯,单声道很棒,但据我所知,它不稳定。它可以工作,但是当你给单进程一个严肃的工作要做时就会出错。

TL;DR - 如果您有以下情况,请勿使用单声道:

  • 在多线程环境中使用AppDomains(程序集加载\卸载)
  • 无法维持“让它失败”模式
  • 流程运行期间偶尔会遇到重负载事件

那么,事实。

我们在 RHEL5、Ubuntu 上使用 mono-2.6.7 (.net v 3.5),在我看来,它是 Novell 构建的最稳定的版本。它存在卸载 AppDomains(段错误)的问题,但是,它失败的情况非常罕见,到目前为止,这是可以接受的(我们)。

好的。但如果您想使用.net 4.0 的功能,则必须切换到版本 2.10.x 或 3.x,这就是问题开始的地方。

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

它在这里,包含使用说明: https://github.com/head-thrash/stress_test_mono

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

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

  • sgen GC 段错误几乎立即处理
  • mono with boehm 寿命更长(从 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 segfauls - 例如(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 逻辑相关,因此,您应该在单声道中远离它们。

顺便说一句,经过测试的程序在 MS .NET 4.5 env 的 Windows 机器上运行了 24 小时,没有任何失败。

所以,总而言之,我想说——谨慎使用 mono。乍一看它很有效,但任何时候都很容易失败。你会留下一堆核心转储和对开源项目的重大信心丧失。

正如其他人所建议的,MoMA 是一个很好的工具。如今,最大的不兼容性来源是 DllImport(或 P/Invoke)到 Win32 库中的应用程序。有些程序集尚未实现,但大多数程序集仅适用于 Windows,并且在 Linux 上确实没有意义。我认为可以相当肯定地说,大多数 ASP.NET 应用程序只需进行有限的修改就可以在 Mono 上运行。

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

在许多情况下,您可以采用现有代码并在 Mono 上运行它,特别是在移植 ASP.NET 应用程序时。

在某些情况下,您可能需要全新的代码部分才能使其工作。例如,如果您使用 System.Windows.Forms,则应用程序将无法在未经修改的情况下运行。同样,如果您使用任何特定于 Windows 的代码(例如注册表访问代码)。但我认为最严重的问题是 UI 代码。这对于 Macintosh 系统来说尤其糟糕。

我们一直在工作中将它用于需要在 Linux 上运行但重用我们在托管 C++ 中构建的一些 .NET 库的项目。我对它的效果感到非常惊讶。我们的主要可执行文件是用 C# 编写的,我们可以毫无问题地引用我们的托管 C++ 二进制文件。Windows 和 Linux 之间 C# 代码的唯一区别是 RS232 串口代码。

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

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

从我使用它的一点点来看,它看起来相对完整并且几乎可用。它只是在某些地方看起来不太正确,而且总体上仍然有点碰运气。老实说,令我惊讶的是它与我们的一些表单一样有效。

是的,肯定是(如果您小心的话)我们在Ra-Ajax(Ajax库中找到)支持Mono 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 上它可以工作,但是一旦我们转移到更大的数据集,它也会崩溃。

这段代码非常简单,例如将一些数据放入 HashSets 中,然后查询这些 HashSets 等,所有本机 C#,没有不安全的地方,没有 API 调用。在 Microsoft CLR 上,它永远不会崩溃,并且可以在巨大的数据集上运行 1000 次。

我们的一个人给 Miguel 发了电子邮件,并附上了导致问题的代码,但尚未得到回复。:(

似乎许多其他人也遇到过这个问题但没有解决方案 - 有人建议使用一种解决方案来使用不同的 GC 设置重新编译 Mono,但这似乎只是增加了崩溃之前的阈值。

只需查看 www. Plasticscm.com 即可。一切(客户端、服务器、GUI、合并工具)都是用 mono 编写的。

它实际上取决于您在 .NET 框架中使用的命名空间和类。我有兴趣将我的一项 Windows 服务转换为在我的电子邮件服务器(即 Suse)上运行,但我们遇到了一些尚未完全实现的 API 的硬障碍。Mono 网站上有一个图表,列出了所有课程及其完成程度。如果您的申请涵盖在内,那就去吧。

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

我们遇到的另一个问题是许可软件:如果您引用其他人的 DLL,则无法通过编码解决隐藏在该程序集中的不兼容性问题。

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

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

不,单声道还没有准备好进行严肃的工作。我使用 F# 在 Windows 上编写了一些程序并在 Mono 上运行它们。这些程序非常密集地使用磁盘、内存和CPU。我看到了单声道库(托管代码)崩溃、本机代码崩溃和虚拟机崩溃。当单声道工作时,程序在 Windows 中至少比 .Net 慢两倍,并且使用更多内存。对于严肃的工作,请远离单声道。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top