314

有没有人在大型或中型项目中使用过 Mono,开源 .NET 实现?我想知道它是否已准备好用于现实世界的生产环境。它是否稳定、快速、兼容、......足够使用?将项目移植到 Mono 运行时是否需要付出很多努力,或者它是否真的非常兼容,足以为微软的运行时获取并运行已经编写的代码?

4

17 回答 17

402

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

对于第一种情况,您可以使用Mono Migration Analyzer 工具(Moma) 来评估您的应用程序离在 Mono 上运行还有多远。如果评估结果非常好,您应该开始测试和 QA 并准备发货。

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

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

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

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

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

Windows.Forms 移植有时会比较棘手,因为开发人员喜欢逃离 .NET 沙箱并 P/Invoke 他们的大脑来配置一些有用的东西,例如改变光标闪烁率,在 wParam 中以 BCD 格式编码的两个贝塞尔点表示。或者类似的垃圾。

于 2008-09-18T16:05:56.313 回答
65

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

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

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

检查特定项目的最简单方法是运行Mono Migration Analyzer (MoMA)。好处是它将通知 Mono 团队有关阻止您使用 Mono(如果有)的问题,从而让他们优先考虑他们的工作。

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

Mono 已在多种商业和开源产品中得到积极使用。它已在一些大型应用程序中使用,例如Wikipedia 和 Mozilla 开发人员中心,并已用于 Sansa MP3 播放器等嵌入式应用程序,并为数千个已发布的游戏提供支持。

在语言级别,Mono 编译器完全符合 C# 5.0 语言规范

于 2008-08-20T18:21:49.800 回答
39

在桌面端,如果您承诺使用 GTK#,Mono 会非常好用。Windows.Forms 实现仍然存在一些问题(例如,TrayIcon 不起作用),但它已经走了很长一段路。此外,GTK# 是比 Windows 窗体更好的工具包。

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

无论哪种方式,Mono 都很棒,而且很稳定。

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

  • 使用 GTK# 而不是 Windows.Forms
  • 确保正确区分您的文件名
  • 使用Path.Separator代替硬编码"\",也使用Environment.NewLine代替"\n"
  • 不要对 Win32 API 使用任何 P/Invoked 调用。
  • 不要使用 Windows 注册表。
于 2008-08-20T18:17:32.510 回答
23

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

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

  • MonoDevelop(IDE)有一些部分的 msbuild 支持,但基本上会在简单的 hello-world 之外的任何“REAL”构建 conf(自定义构建任务、动态“属性”如 $(SolutionDir)、真实配置等等) -结束)
  • xbuild应该是mono-supplied-msbuild-fully-compatible-build-system 更可怕,所以从命令行构建实际上比使用 GUI 更糟糕,这是一种非常“非正统”的状态用于 Linux 环境的联合...

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

  • 编译器对某些结构感到厌烦
  • 以及某些更高级/新的 .NET 类向您抛出意想不到的废话(XLinq 有人吗?)
  • 一些不成熟的运行时“功能”(x64 上的 3GB 堆限制...... WTF!)

但是他说一般来说事情开始很快,解决方案/解决方法很丰富

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

我有运行单声道的服务器,每天处理 300GB 的数据,有大量的 p/invokes,一般来说做很多工作并保持 5-6 个月,即使是“前沿”单声道。

希望这可以帮助。

于 2008-11-05T12:10:50.170 回答
21

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

  • Windows 窗体的实现现在非常好。(请参阅Paint-Mono了解 Paint.net 的一个端口,这是一个相当复杂的 Windows 窗体应用程序。所需要的只是一些 P-Invoke 和不受支持的系统调用的仿真层)。
  • Path.Combine 和 Path.Seperator 用于连接路径和文件名。
  • windows 注册表是可以的,只要您只使用它来存储和检索应用程序中的数据(即,您无法从中获取有关 Windows 的任何信息,因为它基本上是 Mono 应用程序的注册表)。
于 2008-08-31T16:53:49.893 回答
12

如果你想使用 WPF,那你倒霉了 Mono 目前还没有实施它的计划。

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

于 2008-09-29T03:55:57.970 回答
9

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

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

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

所以,事实。

我们在 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 segfaults 几乎立即处理
  • 带有 boehm 的单声道寿命更长(从 2 到 5 小时),但最终会出现段错误

如上所述, sgen gc 只是不起作用(从源代码构建的单声道):

* 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: 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,单声道取自 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 环境中的 Windows 机器上运行了 24 小时,没有任何失败。

所以,总而言之,我想说 - 谨慎使用单声道。乍一看它有效,但很容易失败。你会留下一堆核心转储和对开源项目的重大信心损失。

于 2013-09-26T07:02:32.697 回答
5

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

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

于 2008-08-24T02:12:54.607 回答
4

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

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

于 2008-08-20T18:17:22.277 回答
4

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

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

于 2008-08-20T19:23:30.027 回答
2

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

从我玩过的一点点来看,它似乎相对完整并且几乎可以使用。它只是在某些地方看起来不太对劲,而且总体上还是有点受欢迎或遗漏。让我吃惊的是,它和我们的某些表格一样有效,尽管老实说。

于 2008-08-20T18:36:50.623 回答
2

是的,它肯定是(如果你小心的话)我们在 Ra-Ajax 中支持 Mono(在http://ra-ajax.org找到的 Ajax 库),而且我们几乎没有遇到任何问题。您需要小心 .Net 中的一些“最疯狂的事情”,例如 WSE 等,而且您现有的一些项目可能不会 100% 兼容 Mono,但是如果您在开发过程中测试它们,新项目将主要是与 Mono 兼容而不会出现问题。通过使用 Mono 支持 Linux 等的好处真的很酷;)

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

于 2008-10-23T17:35:00.703 回答
2

对于我们正在构建的应用程序类型,遗憾的是 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 的一种解决方案,但这似乎只是增加了崩溃之前的阈值。

于 2008-10-24T11:58:34.630 回答
2

只需检查 www.plasticscm.com。一切(客户端、服务器、GUI、合并工具)都是用单声道编写的。

于 2008-10-26T12:17:59.273 回答
1

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

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

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

于 2008-08-20T18:21:50.660 回答
1

我想如果你有一个包含一些 3rd 方组件的应用程序,你可能会被塞满。我怀疑很多供应商会在开发时考虑到 Mono

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

于 2008-08-20T18:38:48.950 回答
1

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

于 2010-06-12T21:43:03.150 回答